除非我弄错了,在Python中创建一个函数是这样的:

def my_func(param1, param2):
    # stuff

但是,实际上并没有给出这些参数的类型。而且,如果我没记错的话,Python是一种强类型语言,因此,Python似乎不应该让你传入与函数创建者期望的不同类型的参数。然而,Python如何知道函数的用户正在传递正确的类型呢?假设函数实际使用了形参,如果它是错误的类型,程序会死吗?必须指定类型吗?


当前回答

Python是强类型的,因为每个对象都有一个类型,每个对象都知道它的类型,不可能意外或故意使用一个类型的对象,“好像”它是一个不同类型的对象,对象上的所有基本操作都委托给它的类型。

这和名字无关。Python中的名称没有“类型”:如果定义了名称,则名称指向对象,并且对象确实具有类型(但这实际上并不强制名称具有类型:名称就是名称)。

A name in Python can perfectly well refer to different objects at different times (as in most programming languages, though not all) -- and there is no constraint on the name such that, if it has once referred to an object of type X, it's then forevermore constrained to refer only to other objects of type X. Constraints on names are not part of the concept of "strong typing", though some enthusiasts of static typing (where names do get constrained, and in a static, AKA compile-time, fashion, too) do misuse the term this way.

其他回答

要有效地使用typing模块(Python 3.5新增),请包含all(*)。

from typing import *

你将准备使用:

List, Tuple, Set, Map - for list, tuple, set and map respectively.
Iterable - useful for generators.
Any - when it could be anything.
Union - when it could be anything within a specified set of types, as opposed to Any.
Optional - when it might be None. Shorthand for Union[T, None].
TypeVar - used with generics.
Callable - used primarily for functions, but could be used for other callables.

然而,你仍然可以使用类型名称,如int, list, dict,…

如果有人想指定变量类型,我已经实现了一个包装器。

import functools
    
def type_check(func):

    @functools.wraps(func)
    def check(*args, **kwargs):
        for i in range(len(args)):
            v = args[i]
            v_name = list(func.__annotations__.keys())[i]
            v_type = list(func.__annotations__.values())[i]
            error_msg = 'Variable `' + str(v_name) + '` should be type ('
            error_msg += str(v_type) + ') but instead is type (' + str(type(v)) + ')'
            if not isinstance(v, v_type):
                raise TypeError(error_msg)

        result = func(*args, **kwargs)
        v = result
        v_name = 'return'
        v_type = func.__annotations__['return']
        error_msg = 'Variable `' + str(v_name) + '` should be type ('
        error_msg += str(v_type) + ') but instead is type (' + str(type(v)) + ')'
        if not isinstance(v, v_type):
                raise TypeError(error_msg)
        return result

    return check

使用它作为:

@type_check
def test(name : str) -> float:
    return 3.0

@type_check
def test2(name : str) -> str:
    return 3.0

>> test('asd')
>> 3.0

>> test(42)
>> TypeError: Variable `name` should be type (<class 'str'>) but instead is type (<class 'int'>)

>> test2('asd')
>> TypeError: Variable `return` should be type (<class 'str'>) but instead is type (<class 'float'>)

EDIT

如果没有声明任何参数的(或返回值的)类型,上面的代码就不能工作。下面的编辑可以提供帮助,另一方面,它只对kwarg有效,不检查args。

def type_check(func):

    @functools.wraps(func)
    def check(*args, **kwargs):
        for name, value in kwargs.items():
            v = value
            v_name = name
            if name not in func.__annotations__:
                continue
                
            v_type = func.__annotations__[name]

            error_msg = 'Variable `' + str(v_name) + '` should be type ('
            error_msg += str(v_type) + ') but instead is type (' + str(type(v)) + ') '
            if not isinstance(v, v_type):
                raise TypeError(error_msg)

        result = func(*args, **kwargs)
        if 'return' in func.__annotations__:
            v = result
            v_name = 'return'
            v_type = func.__annotations__['return']
            error_msg = 'Variable `' + str(v_name) + '` should be type ('
            error_msg += str(v_type) + ') but instead is type (' + str(type(v)) + ')'
            if not isinstance(v, v_type):
                    raise TypeError(error_msg)
        return result

    return check

从静态或编译时类型检查的意义上讲,Python不是强类型的。

大多数Python代码都属于所谓的“Duck Typing”——例如,你寻找一个对象上读取的方法——你不关心对象是磁盘上的文件还是套接字,你只想从中读取N个字节。

在这个页面上,有一个臭名昭著的例外值得提及。

当str函数调用__str__类方法时,它会巧妙地检查其类型:

>>> class A(object):
...     def __str__(self):
...         return 'a','b'
...
>>> a = A()
>>> print a.__str__()
('a', 'b')
>>> print str(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __str__ returned non-string (type tuple)

就好像Guido提示我们,如果程序遇到意外类型,应该引发哪个异常。

在Python中,所有东西都有类型。如果参数类型支持,Python函数将执行它被要求执行的任何操作。

示例:foo将添加所有可以__add__ed;)的东西,而不太关心它的类型。这意味着,为了避免失败,你应该只提供那些支持加法的东西。

def foo(a,b):
    return a + b

class Bar(object):
    pass

class Zoo(object):
    def __add__(self, other):
        return 'zoom'

if __name__=='__main__':
    print foo(1, 2)
    print foo('james', 'bond')
    print foo(Zoo(), Zoo())
    print foo(Bar(), Bar()) # Should fail