除非我弄错了,在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
,在语法上允许,但它不适合以编程方式进行解析(我们将在稍后回溯).
重要的是要注意,TypeError
如果你传递一个浮点数,Python就不会引发一个浮点数index
,其原因之一是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,它引入了类型提示的标准模块.
此语法来自正在开发的可选静态类型检查器工具mypy(GitHub)(与PEP 484兼容).
打字模块附带了一个非常全面的类型提示集合,包括:
List
,Tuple
,Set
,Map
-为list
,tuple
,set
和map
分别.
Iterable
- 对发电机很有用.
Any
- 什么时候可以.
Union
- 当它可以是指定的一组类型中的任何东西时,而不是Any
.
Optional
- 当它可能是无.简写Union[T, None]
.
TypeVar
- 与泛型一起使用.
Callable
- 主要用于功能,但可用于其他callables.
此列表代表最常见的类型提示,但它远非详尽无遗.可以在打字模块的文档中找到完整的列表.
以下是使用输入模块中引入的注释方法的旧示例:
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代码时,可以通过编写如下格式的文档字符串来获得上述功能中的一些:
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]
正如您所看到的,这需要额外的一些行(确切的数字取决于您希望的显式方式以及格式化文档字符串的方式).但现在应该清楚PEP 3107如何提供一种替代方案,这种方式在很多(所有)方面都很优越.结合PEP 484尤其如此,正如我们所见,PEP 484提供了一个标准模块,它定义了这些类型提示/注释的语法,这些提示/注释可以以明确,精确和灵活的方式使用,强大的组合.
在我个人看来,这是Python史上最伟大的功能之一.我等不及人们开始利用它的力量了.对不起,答案很长,但这就是当我兴奋时会发生的事情.
可以在此处找到大量使用类型提示的Python代码示例.
Python是强类型的,因为每个对象都有一个类型,每个对象都知道它的类型,不可能偶然或故意使用类型的对象"好像"它是一个不同类型的对象,并且对象上的所有基本操作都是委托给它的类型.
这与名字无关.Python中的名称不具有"类型":如果名称定义,名称引用对象,并且对象确实具有类型(但实际上不强制名称上的类型:a名字是一个名字).
Python中的名称可以很好地在不同的时间引用不同的对象(如在大多数编程语言中,但不是全部) - 并且对名称没有约束,如果它曾经引用类型为X的对象,然后它永远受限于仅引用X类型的其他对象.名称上的约束不是"强类型"概念的一部分,尽管有些静态类型的爱好者(名称确实受到限制,并且在静态的AKA编译中)时间,时尚,也是这样滥用这个词.
您没有指定类型.如果该方法尝试访问未在传入的参数上定义的属性,则该方法将仅在运行时失败.
所以这个简单的功能:
def no_op(param1, param2): pass
......无论传递两个参数,都不会失败.
但是,这个功能:
def call_quack(param1, param2): param1.quack() param2.quack()
...如果param1
并且param2
都没有命名可调用属性,则会在运行时失败quack
.
许多语言都有变量,这些变量属于特定类型且具有值.Python没有变量; 它有对象,您使用名称来引用这些对象.
在其他语言中,当你说:
a = 1
然后一个(通常是整数)变量将其内容更改为值1.
在Python中,
a = 1
表示"使用名称a来引用对象1 ".您可以在交互式Python会话中执行以下操作:
>>> type(1)
type
用对象调用该函数1
; 因为每个对象都知道它的类型,所以很容易type
找到所述类型并将其返回.
同样,无论何时定义函数
def funcname(param1, param2):
该函数接收两个对象,它们的名称param1
和param2
,无论其类型.如果要确保收到的对象是特定类型,请将您的函数编码为所需类型,并捕获所引发的异常(如果不是).抛出的异常通常是TypeError
(您使用了无效操作)和AttributeError
(您尝试访问不存在的成员(方法也是成员)).
从静态或编译时类型检查的意义上讲,Python不是强类型的.
大多数Python代码属于所谓的"Duck Typing" - 例如,你read
在对象上寻找一个方法- 你不关心对象是磁盘上的文件还是套接字,你只想读N来自它的字节.
正如Alex Martelli解释的那样,
正常的,Pythonic,首选解决方案几乎总是"鸭子打字":尝试使用该参数,就好像它是某种期望的类型一样,在try/except语句中执行它,捕获可能出现的所有异常,如果参数实际上不是那种类型(或任何其他类型很好地模仿它;-),并在except子句中,尝试其他东西(使用参数"好像"它是其他类型).
阅读他的帖子的其余部分以获取有用的信息.