Function parameter types in Python
除非我弄错了,否则在Python中创建函数的工作方式如下:
1 2 | def my_func(param1, param2): # stuff |
但是,实际上并没有给出这些参数的类型。另外,如果我还记得,python是一种强类型语言,因此,似乎python不应该让您传入与函数创建者期望的不同类型的参数。但是,python如何知道函数的用户正在传入正确的类型?如果程序是错误的类型,假设函数实际使用了参数,它会死吗?必须指定类型吗?
其他答案在解释duck输入和Tzot的简单答案方面做得很好:好的。
Python does not have variables, like other languages where variables have a type and a value; it has names pointing to objects, which know their type.
Ok.
然而,自2010年以来,有一件有趣的事情发生了变化(第一次提出这个问题时),即PEP 3107的实现(在Python3中实现)。现在可以实际指定参数的类型和函数的返回类型,如下所示:好的。
1 2 | def pick(l: list, index: int) -> int: return l[index] |
我们可以看到,
因此,这里暗示
1 2 | def pick(l:"list of ints", index: int = 0) -> int: return l[index] |
注意,我们现在把一个字符串作为
重要的是要注意,如果您将float传递到
1 2 3 4 | def pick(l: list, index: int = 0) -> int: if not isinstance(l, list): raise TypeError return l[index] |
更多关于为什么你很少这样做的原因,以及你应该做什么在下一节和评论中讨论。好的。
PEP 3107不仅提高了代码的可读性,而且有几个适合的用例,您可以在这里阅读。好的。
在python 3.5中,由于引入了PEP484,类型注释得到了更多的关注,PEP484引入了一个用于类型提示的标准模块。好的。
这些类型提示来自于类型检查器mypy(github),它现在符合PEP 484。好的。
在输入模块中,有相当全面的类型提示集合,包括:好的。
List 、Tuple 、Set 、Map —分别用于List 、Tuple 、Set 和Map 。Iterable —适用于发电机。Any —什么时候可以。Union —它可以是一组特定类型中的任何内容,而不是Any 。Optional —可能没有。Union[T, None] 的简写。TypeVar —与仿制药一起使用。Callable —主要用于函数,但也可用于其他可调用文件。
这些是最常见的类型提示。在输入模块的文档中可以找到完整的列表。好的。
下面是使用输入模块中介绍的注释方法的旧示例:好的。
1 2 3 4 | from typing import List def pick(l: List[int], index: int) -> int: return l[index] |
一个强大的特性是
1 2 3 4 5 | 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)) |
上面的例子使用
以前,当一个文档化的python代码使用例如sphinx时,可以通过编写如下格式的docstrings来获得上述一些功能:好的。
1 2 3 4 5 6 7 8 9 10 | 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] |
如您所见,这需要一些额外的行(确切的数字取决于您想要的明确程度以及您如何格式化docstring)。但现在你应该清楚地知道,PEP 3107是如何提供一种在许多方面(全部)都有选择的。方法优越。这一点尤其适用于与PEP484的组合,正如我们所看到的,PEP484提供了一个标准模块,该模块为这些类型提示/注释定义了一种语法,可以以一种明确、精确而又灵活的方式使用,从而实现强大的组合。好的。
在我个人看来,这是Python有史以来最伟大的特性之一。我等不及人们开始利用它的力量。很抱歉回答得太长了,但这就是我兴奋时所发生的事情。好的。
这里可以找到大量使用类型暗示的Python代码示例。好的。好啊。
python是强类型的,因为每个对象都有一个类型,每个对象都知道它的类型,不可能意外地或故意地使用一个类型为"好像"的对象,它是一个不同类型的对象,并且对该对象的所有基本操作都委托给它的类型。
这与名字无关。python中的一个名称没有"有一个类型":如果定义了一个名称,则该名称引用了一个对象,而该对象确实有一个类型(但实际上并不强制在该名称上使用一个类型:名称是一个名称)。
python中的名称可以在不同的时间完美地引用不同的对象(在大多数编程语言中,虽然不是全部),并且对名称没有约束,因此,如果它曾经引用过X类型的对象,那么它将被进一步约束为只引用X类型的其他对象。对名称的约束不是T的一部分。他提出了"强类型"的概念,尽管一些热衷于静态类型的人(在静态类型中,名字也会受到约束,也就是编译时和时尚的人)确实是这样滥用这个词的。
您没有指定类型。只有当该方法试图访问未在传入参数上定义的属性时(在运行时),该方法才会失败。
所以这个简单的函数:
1 2 | def no_op(param1, param2): pass |
…无论传入哪两个参数,都不会失败。
但是,此功能:
1 2 3 | def call_quack(param1, param2): param1.quack() param2.quack() |
…如果
许多语言都有变量,这些变量属于特定类型并具有值。python没有变量;它有对象,您使用名称来引用这些对象。
在其他语言中,当你说:
1 | a = 1 |
然后(通常为整数)变量将其内容更改为值1。
在Python中,
1 | a = 1 |
表示"使用名称a引用对象1"。您可以在交互式Python会话中执行以下操作:
1 2 | >>> type(1) <type 'int'> |
函数
同样,无论何时定义函数
1 | def funcname(param1, param2): |
函数接收两个对象,并将它们命名为
python不是静态或编译时类型检查的强类型。
大多数python代码都属于所谓的"duck-typing"——例如,您在一个对象上寻找一个方法
正如亚历克斯·马泰利解释的那样,
The normal, Pythonic, preferred solution is almost invariably"duck typing": try using the argument as if it was of a certain desired type, do it in a try/except statement catching all exceptions that could arise if the argument was not in fact of that type (or any other type nicely duck-mimicking it;-), and in the except clause, try something else (using the argument"as if" it was of some other type).
阅读他文章的其余部分以获取有用的信息。
python不关心您将什么传递给它的函数。当您调用
有一个臭名昭著的例外,从鸭子打字值得一提的这一页。
当
1 2 3 4 5 6 7 8 9 10 11 | >>> 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有duck类型的概念;基本上,处理参数的代码会对它们做出某些假设——可能通过调用某个参数要实现的某些方法。如果参数类型错误,则将引发异常。
通常情况下,由您的代码来确保您传递的是正确类型的对象——没有编译器可以提前强制执行这一点。
我在其他答案中没有看到这一点,所以我会把它加到罐子里。
正如其他人所说,python不在函数或方法参数上强制类型。假设你知道你在做什么,如果你真的需要知道被传递的东西的类型,你将检查它并决定为你自己做什么。
执行此操作的主要工具之一是isInstance()函数。
例如,如果我编写的方法希望获得原始的二进制文本数据,而不是普通的UTF-8编码字符串,那么我可以在输入时检查参数的类型,并根据我找到的内容进行调整,或者引发一个要拒绝的异常。
1 2 3 4 | def process(data): if not isinstance(data, bytes) and not isinstance(data, bytearray): raise TypeError('Invalid type: data must be a byte string or bytearray, not %r' % type(data)) # Do more stuff |
python还提供各种工具来挖掘对象。如果你很勇敢,你甚至可以使用importlib动态地创建你自己的任意类的对象。我这样做是为了从JSON数据中重新创建对象。在C++这样的静态语言中,这样的事情将是一场噩梦。
在Python中,一切都有一个类型。如果参数类型支持的话,python函数可以做任何需要做的事情。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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 |