我应该强制Python类型检查吗?

我应该强制Python类型检查吗?,python,types,semantics,Python,Types,Semantics,也许是我在使用强类型语言(Java)的余生,我经常发现自己编写函数,然后强制执行类型检查。例如: def orSearch(d, query): assert (type(d) == dict) assert (type(query) == list) 我应该继续这样做吗?做/不做这件事有什么好处?这是一种非惯用的做事方式。通常在Python中,您会使用try/except测试 def orSearch(d, query): try: d.get(som

也许是我在使用强类型语言(Java)的余生,我经常发现自己编写函数,然后强制执行类型检查。例如:

def orSearch(d, query):
    assert (type(d) == dict)
    assert (type(query) == list)

我应该继续这样做吗?做/不做这件事有什么好处?

这是一种非惯用的做事方式。通常在Python中,您会使用
try/except
测试

def orSearch(d, query):
    try:
        d.get(something)
    except TypeError:
        print("oops")
    try:
        foo = query[:2]
    except TypeError:
        print("durn")

就我个人而言,我对断言有一种厌恶,程序员似乎可以看到麻烦的到来,但却不愿意去思考如何处理它们,另一个问题是,如果其中一个参数是从您期望的参数派生的类,那么您的示例将断言,即使这样的类应该可以工作在您上面的示例中,我会选择以下内容:

def orSearch(d, query):
    """ Description of what your function does INCLUDING parameter types and descriptions """
    result = None
    if not isinstance(d, dict) or not isinstance(query, list):
        print "An Error Message"
        return result
    ...
注意:仅当类型完全符合预期时,类型才匹配,isinstance也适用于派生类。e、 g:

>>> class dd(dict):
...    def __init__(self):
...        pass
... 
>>> d1 = dict()
>>> d2 = dd()
>>> type(d1)
<type 'dict'>
>>> type(d2)
<class '__main__.dd'>
>>> type (d1) == dict
True
>>> type (d2) == dict
False
>>> isinstance(d1, dict)
True
>>> isinstance(d2, dict)
True
>>> 
>dd类(dict):
...    定义初始化(自):
...        通过
... 
>>>d1=dict()
>>>d2=dd()
>>>类型(d1)
>>>类型(d2)
>>>类型(d1)=dict
真的
>>>类型(d2)=dict
假的
>>>isinstance(d1,dict)
真的
>>>isinstance(d2,dict)
真的
>>> 

可以考虑抛出自定义异常而不是断言。您甚至可以通过检查参数是否具有所需的方法来进行更多的概括


顺便说一句我可能很挑剔,但我总是尽量避免在C/C++中使用assert,因为如果它留在代码中,那么几年后就会有人做出应该被它捕获的更改,没有在调试中对它进行足够好的测试(甚至根本没有测试),编译为可交付的,发布模式,-这将删除所有断言,即以这种方式进行的所有错误检查,现在我们有不可靠的代码和一个很头痛的问题。

当您需要进行类型检查时,我同意Steve的方法。我不经常发现需要在Python中进行类型检查,但至少有一种情况我需要这样做。在这种情况下,不检查类型可能会返回不正确的答案,这将在以后的计算中导致错误。这些类型的错误可能很难追踪,我在Python中已经经历过多次。和你一样,我首先学习了Java,不必经常处理它们

假设您有一个简单的函数,它需要一个数组并返回第一个元素

def func(arr): return arr[0]
如果使用数组调用它,将得到数组的第一个元素

>>> func([1,2,3])
1
如果使用字符串或实现magic方法的任何类的对象调用它,也会得到响应

>>> func("123")
'1'
这会给你一个回应,但在这种情况下,它的类型是错误的。这可能发生在具有相同属性的对象上。您可能要到稍后的计算中才能发现错误。如果您在自己的代码中确实遇到了这种情况,这通常意味着在先前的计算中有错误,但是在那里进行检查会更早地发现它。但是,如果你为别人写了一个Python程序包,它可能是你应该考虑的东西。 您不应该因为该检查而遭受巨大的性能损失,但它会使代码更难阅读,这在Python世界中是一件大事。

两件事

首先,如果你愿意花大约200美元,你可以得到一个非常好的python IDE。我使用PyCharm,给我留下了深刻的印象。(这是由为C#制作ReSharper的同一个人开发的)它会在编写代码时分析代码,并查找变量类型错误的地方(在一堆其他东西中)

第二:

在使用PyCharm之前,我遇到了同样的问题——即,我忘记了我编写的函数的特定签名。我可能在什么地方找到了这个,但可能是我写的(我现在记不起来了)。但无论如何,它是一个装饰器,您可以在函数定义周围使用它来为您进行类型检查

这样说吧

@require_type('paramA', str)
@require_type('paramB', list)
@require_type('paramC', collections.Counter)
def my_func(paramA, paramB, paramC):
    paramB.append(paramC[paramA].most_common())
    return paramB
不管怎样,这是装饰师的代码

def require_type(my_arg, *valid_types):
    '''
        A simple decorator that performs type checking.

        @param my_arg: string indicating argument name
        @param valid_types: *list of valid types
    '''
    def make_wrapper(func):
        if hasattr(func, 'wrapped_args'):
            wrapped = getattr(func, 'wrapped_args')
        else:
            body = func.func_code
            wrapped = list(body.co_varnames[:body.co_argcount])

        try:
            idx = wrapped.index(my_arg)
        except ValueError:
            raise(NameError, my_arg)

        def wrapper(*args, **kwargs):

            def fail():
                all_types = ', '.join(str(typ) for typ in valid_types)
                raise(TypeError, '\'%s\' was type %s, expected to be in following list: %s' % (my_arg, all_types, type(arg)))

            if len(args) > idx:
                arg = args[idx]
                if not isinstance(arg, valid_types):
                    fail()
            else:
                if my_arg in kwargs:
                    arg = kwargs[my_arg]
                    if not isinstance(arg, valid_types):
                        fail()

            return func(*args, **kwargs)

        wrapper.wrapped_args = wrapped
        return wrapper
    return make_wrapper
#! /usr/bin/env python3
import functools
import inspect


def static_types(wrapped):
    def replace(obj, old, new):
        return new if obj is old else obj

    signature = inspect.signature(wrapped)
    parameter_values = signature.parameters.values()
    parameter_names = tuple(parameter.name for parameter in parameter_values)
    parameter_types = tuple(
        replace(parameter.annotation, parameter.empty, object)
        for parameter in parameter_values
    )
    return_type = replace(signature.return_annotation, signature.empty, object)

    @functools.wraps(wrapped)
    def wrapper(*arguments):
        for argument, parameter_type, parameter_name in zip(
            arguments, parameter_types, parameter_names
        ):
            if not isinstance(argument, parameter_type):
                raise TypeError(f'{parameter_name} should be of type '
                                f'{parameter_type.__name__}, not '
                                f'{type(argument).__name__}')
        result = wrapped(*arguments)
        if not isinstance(result, return_type):
            raise TypeError(f'return should be of type '
                            f'{return_type.__name__}, not '
                            f'{type(result).__name__}')
        return result
    return wrapper

如果您坚持要在代码中添加类型检查,您可能需要研究它们如何简化您必须编写的代码。其中一个OnStackOverflow引入了一个利用注释的小型模糊类型检查器。以下是一个基于您的问题的示例:

def静态类型(a): def b(a、b、c): 如果a中的b不是实例(c,a[b]):引发类型错误({}应该是{},而不是{})。格式(b,a[b],类型(c))) 返回c return uuu import uuuu('functools')。包装(a)(lambda*c:b(a.uuu注解uuu,'return',a(*[b(a.uu注解uuuu,*d)用于zip中的d(a.uu代码uu.co_u变量名,c)]) >>>@statictypes 定义或搜索(d:dict,查询:dict)->类型(无): 通过 >>>或搜索({},{}) >>>或搜索([],{}) 回溯(最近一次呼叫最后一次): 文件“”,第1行,在 或搜索([],{}) 文件“”,第5行,在 return uuu import uuuu('functools')。包装(a)(lambda*c:b(a.uuu注解uuu,'return',a(*[b(a.uu注解uuuu,*d)用于zip中的d(a.uu代码uu.co_u变量名,c)]) 文件“”,第5行,在 return uuu import uuuu('functools')。包装(a)(lambda*c:b(a.uuu注解uuu,'return',a(*[b(a.uu注解uuuu,*d)用于zip中的d(a.uu代码uu.co_u变量名,c)]) 文件“”,第3行,在b中 如果a中的b不是实例(c,a[b]):引发类型错误({}应该是{},而不是{})。格式(b,a[b],类型(c))) TypeError:d应该是,而不是 >>>或搜索({},[])) 回溯(最近一次呼叫最后一次): 文件“”,第1行,在 或搜索({},[])) 文件“”,第5行,在 return uuu import uuuu('functools')。包装(a)(lambda*c:b(a.uuu注解uuu,'return',a(*[b(a.uu注解uuuu,*d)用于zip中的d(a.uu代码uu.co_u变量名,c)]) 文件“”,第5行,在 return uuu import uuuu('functools')。包装(a)(lambda*c:b(a.uuu注解uuu,'return',a(*[b(a.uu注解uuuu,*d)用于zip中的d(a.uu代码uu.co_u变量名,c)]) 文件“”,第3行,在b中 如果a中的b不是实例(c,a[b]):引发类型错误({}应该是{},而不是{})。格式(b,a[b],类型(c))) TypeError:查询应为,而不是 >>> 你可以看看
assert isinstance(d, dict)