除非我弄错了,创建Python函数的方式如下:
def my_func(param1, param2):
# stuff
然而,您实际上没有给出这些参数的类型。此外,如果我没记错的话,Python是一种强类型语言,因此看起来Python不应该允许您传递与函数创建者预期的不同类型的参数。但是,Python如何知道函数的用户是否传递了正确的类型?如果类型不正确,程序会崩溃吗?您必须指定类型吗?
除非我弄错了,创建Python函数的方式如下:
def my_func(param1, param2):
# stuff
然而,您实际上没有给出这些参数的类型。此外,如果我没记错的话,Python是一种强类型语言,因此看起来Python不应该允许您传递与函数创建者预期的不同类型的参数。但是,Python如何知道函数的用户是否传递了正确的类型?如果类型不正确,程序会崩溃吗?您必须指定类型吗?
其他答案已经对鸭子类型进行了很好的解释,以及tzot提供的简单答案:
Python不像其他语言中变量拥有类型和值,它有指向对象的名称,对象知道自己的类型。
然而,自2010年(问题首次被提出)以来,有一件有趣的事情发生了变化,即实现了PEP 3107(在Python 3中实现)。您现在可以像这样实际指定参数的类型和函数返回类型的类型:
def pick(l: list, index: int) -> int:
return l[index]
pick
需要 2 个参数,一个是列表l
,另一个是整数index
。它应该返回一个整数。l
是由整数组成的列表,我们不用费太多力气就能看出来,但对于更复杂的函数,可能会让人困惑列表应该包含什么。我们还希望index
的默认值为0。为了解决这个问题,您可以选择像这样编写pick
:def pick(l: "list of ints", index: int = 0) -> int:
return l[index]
l
的类型放入其中,这在语法上是允许的,但对于编程解析来说并不好(稍后我们会回到这个问题)。index
,Python不会引发TypeError
,这是Python设计哲学的主要观点之一:“我们都是成年人”,这意味着你应该知道可以传递什么参数到函数中。如果你真的想编写会抛出TypeErrors的代码,可以使用isinstance
函数检查传递的参数是否是正确的类型或其子类,如下所示:def pick(l: list, index: int = 0) -> int:
if not isinstance(l, list):
raise TypeError
return l[index]
在接下来的部分和评论中,将更详细地讨论为什么你应该很少这样做以及你应该做什么。
PEP 3107不仅可以提高代码可读性,还有几个适合的用例,您可以在此处阅读有关详细信息。
在Python 3.5中,类型注释引起了更多的关注,这得益于PEP 484的引入,该规范介绍了一个标准模块typing
用于类型提示。
这些类型提示来自类型检查器mypy(GitHub),现在符合PEP 484规范。
typing
模块提供了一系列相当全面的类型提示,包括:
List
,Tuple
,Set
,Dict
- 分别用于 list
,tuple
,set
和 dict
。Iterable
- 用于生成器。Any
- 表示可以是任何类型。Union
- 表示可以是指定类型集合中的任何一种类型,与 Any
不同。Optional
- 表示可能是 None。等同于 Union[T, None]
。TypeVar
- 用于泛型。Callable
- 主要用于函数,但也可用于其他可调用对象。这些是最常见的类型提示。完整列表可在 typing 模块文档 中找到。
以下是使用 typing 模块引入的注释方法的旧示例:
from typing import List
def pick(l: List[int], index: int) -> int:
return l[index]
一个强大的功能是Callable
,它允许你对接受函数作为参数的方法进行类型注解。例如:
from typing import Callable, Any, Iterable
def imap(f: Callable[[Any], Any], l: Iterable[Any]) -> List[Any]:
"""An immediate version of map, don't pass it any infinite iterables!"""
return list(map(f, l))
TypeVar
而不是Any
来变得更加精确,但由于我认为已经在答案中填充了太多关于类型提示启用的新奇功能的信息,因此这留给读者作为一个练习。
以前,当使用 Sphinx 等工具记录Python代码时,可以通过编写格式如下的docstrings来获得上述某些功能:
def pick(l, index):
"""
:param l: list of integers
:type l: list
:param index: index at which to pick an integer from *l*
:type index: int
:returns: integer at *index* in *l*
:rtype: int
"""
return l[index]
可以在这里找到大量使用类型提示的Python代码示例。
Python是强类型的,因为每个对象都有一个类型,每个对象都知道自己的类型,无法意外或故意使用一个类型的对象“伪装”成另一种类型的对象,并且所有基本操作都委托给其类型。
这与名称无关。在Python中,名称没有“类型”:当定义名称时,名称引用一个对象,而对象确实具有类型(但这实际上不会将类型强制到名称上:名称只是名称)。
在Python中,名称可以完全在不同时间引用不同的对象(与大多数编程语言一样,虽然不是所有编程语言都是如此),并且名称上并没有约束,如果它曾经引用过X类型的对象,则永远被限制仅引用其他X类型的对象。对于名称的约束不属于“强类型”的概念,尽管某些静态类型的爱好者(其中名称确实受到约束,而且是以静态,即编译时方式)会误用这个术语。
try
/except
语句,则程序不会“死亡”。在Python 3.5中,您现在可以选择“指定参数类型”,但是如果违反了规范,不会出现实际上的错误;类型注释只是用于帮助分离执行分析等操作的工具,它不会改变Python本身的行为。 - Alex Martelli您没有指定类型。该方法只会在运行时尝试访问未在传递的参数上定义的属性时失败。
因此,这个简单的函数:
def no_op(param1, param2):
pass
无论传入什么两个参数,该函数都不会失败。
然而,这个函数:
def call_quack(param1, param2):
param1.quack()
param2.quack()
如果param1
和param2
没有都具有名为quack
的可调用属性,则在运行时会出现失败。
如果有人想指定变量类型,我已经实现了一个包装器。
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'>)
如果没有声明参数(或返回值)类型,则上述代码将无法工作。以下编辑可以解决这个问题,但它只适用于kwargs,并不检查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
a = 1
通常情况下,一个整数变量的值会改变为1。
在Python中,
a = 1
意思是“使用名称a来引用对象1”。您可以在交互式Python会话中执行以下操作:
>>> type(1)
<type 'int'>
函数type
被调用并传入对象1
; 由于每个对象都知道自己的类型,所以type
很容易找到该类型并返回它。
同样地,当你定义一个函数时
def funcname(param1, param2):
函数接收两个对象,并将它们命名为 param1
和 param2
,无论它们的类型是什么。如果您希望确保所接收的对象是特定类型,请编写函数,就好像它们是所需类型,并捕获抛出的异常(如果它们不是所需类型)。通常抛出的异常是 TypeError
(使用了无效操作)和 AttributeError
(尝试访问不存在的成员(方法也是成员))。read
--你不关心这个对象是磁盘上的文件还是套接字,你只想从中读取N个字节。阅读他的帖子以获取有用的信息。正常的、Pythonic的、首选的解决方案几乎总是“鸭子类型”:尝试将参数用作某种期望的类型,使用try/except语句执行此操作,捕获可能出现的所有异常,如果参数实际上不是该类型(或任何其他很好地模仿它的类型),则在except子句中尝试其他内容(将参数“视为”其他类型)。
Python不会在意你传递给它的函数参数类型。当你调用my_func(a,b)
时,param1和param2变量将保存a和b的值。Python并不知道你使用了正确的参数类型,并期望程序员自己处理。如果你的函数将被传入不同类型的参数,你可以使用try/except块包装访问它们的代码,并以任何你想要的方式评估这些参数。
在这个页面中,有一个著名的例外不符合鸭子类型的规则。
当 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在暗示我们,当程序遇到意外类型时应该引发哪种异常。
TypeError
错误,那么使用像“一行定义”一样的pick(l: list, index: int) -> int
有什么意义呢?或者我理解错了,我不知道。 - Eray Erdin__annotations__
属性中)。 - erbdef f(a) -> Tuple[int, int]:
。 - erb