Python中的函数参数类型

问题描述 投票:223回答:11

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

def my_func(param1, param2):
    # stuff

但是,您实际上并未提供这些参数的类型。另外,如果我记得,Python是一种强类型语言,因此,似乎Python不应该让你传递一个与函数创建者所期望的不同类型的参数。但是,Python如何知道函数的用户是否传递了正确的类型?假设函数实际使用参数,程序是否会因错误类型而死亡?你必须指定类型吗?

python function parameters
11个回答
141
投票

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

这与名字无关。 Python中的名称不具有“类型”:如果名称定义,名称引用对象,并且对象确实具有类型(但实际上不强制名称上的类型:a名字是一个名字)。

Python中的名称可以很好地在不同的时间引用不同的对象(如在大多数编程语言中,但不是全部) - 并且对名称没有约束,如果它曾经引用了类型为X的对象,然后它永远受限于仅引用X类型的其他对象。名称上的约束不是“强类型”概念的一部分,尽管有些静态类型的爱好者(名称确实受到限制,并且在静态的AKA编译中)时间,时尚,也是这样滥用这个词。


1
投票

在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

1
投票

我没有在其他答案中看到这一点,所以我将其添加到底池中。

正如其他人所说,Python不会对函数或方法参数强制执行类型。假设你知道你在做什么,如果你真的需要知道传入的东西的类型,你将检查它并决定自己做什么。

执行此操作的主要工具之一是isinstance()函数。

例如,如果我编写一个期望获取原始二进制文本数据的方法,而不是正常的utf-8编码字符串,我可以在路上检查参数的类型,并适应我找到的,或者提高拒绝例外。

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 ++这样的静态语言的噩梦。


-1
投票

为了有效地使用输入模块(Python 3.5中的新功能),请包含所有(*)。

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.

但是,你仍然可以使用类型名称,如intlistdict,...


572
投票

其他答案在解释鸭子打字和the simple answer by tzot方面做得很好:

Python没有变量,就像变量具有类型和值的其他语言一样;它的名称指向对象,这些对象知道它们的类型。

然而,自2010年(首次提出问题时),有一件有趣的事情发生了变化,即PEP 3107的实现(在Python 3中实现)。您现在可以实际指定参数的类型和函数的返回类型的类型,如下所示:

def pick(l: list, index: int) -> int:
    return l[index]

我们在这里可以看到pick有两个参数,一个列表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不仅提高了代码的可读性,而且还有几个适合的用例,你可以阅读有关here的内容。


类型注释在Python 3.5中得到了更多的关注,引入了PEP 484,它引入了类型提示的标准模块。

这些类型提示来自类型检查器mypyGitHub),现在符合PEP 484

打字模块附带了一个非常全面的类型提示集合,包括:

  • ListTupleSetMap - 分别为listtuplesetmap
  • Iterable - 对发电机很有用。
  • Any - 什么时候可以。
  • Union - 当它可以是指定的一组类型中的任何东西,而不是Any
  • Optional - 什么时候可能是无。 Union[T, None]的简写。
  • TypeVar - 与泛型一起使用。
  • Callable - 主要用于功能,但可用于其他callables。

这些是最常见的类型提示。完整列表可以在documentation for the typing module找到。

以下是使用输入模块中引入的注释方法的旧示例:

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来变得更精确,但是这已经留给读者了,因为我相信我已经用关于通过类型提示启用的精彩新功能的太多信息填充了我的答案。


以前,当一个记录的Python代码与Sphinx有关时,可以通过编写如下格式的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]

正如您所看到的,这需要额外的一些行(确切的数字取决于您希望的显式方式以及格式化文档字符串的方式)。但现在应该清楚PEP 3107如何提供一种替代方案,这种方式在很多(所有)方面都很优越。结合PEP 484尤其如此,正如我们所看到的,它提供了一个标准模块,它定义了这些类型提示/注释的语法,可以以明确,精确和灵活的方式使用,从而实现强大功能。组合。

在我个人看来,这是Python史上最伟大的功能之一。我等不及人们开始利用它的力量了。对不起,答案很长,但这就是当我兴奋时会发生的事情。


可以在here找到一个大量使用类型提示的Python代码示例。


13
投票

您没有指定类型。如果该方法尝试访问未在传入的参数上定义的属性,则该方法将仅在运行时失败。

所以这个简单的功能:

def no_op(param1, param2):
    pass

......无论传递两个参数,都不会失败。

但是,这个功能:

def call_quack(param1, param2):
    param1.quack()
    param2.quack()

...如果param1param2都没有名为quack的可调用属性,则会在运行时失败。


8
投票

许多语言都有变量,这些变量属于特定类型且具有值。 Python没有变量;它有对象,您使用名称来引用这些对象。

在其他语言中,当你说:

a = 1

然后一个(通常是整数)变量将其内容更改为值1。

在Python中,

a = 1

表示“使用名称a来引用对象1”。您可以在交互式Python会话中执行以下操作:

>>> type(1)
<type 'int'>

函数type用对象1调用;因为每个对象都知道它的类型,所以type很容易找到所述类型并将其返回。

同样,无论何时定义函数

def funcname(param1, param2):

该函数接收两个对象,并将它们命名为param1param2,无论它们的类型如何。如果要确保收到的对​​象是特定类型,请将您的函数编码为所需类型,并捕获所引发的异常(如果不是)。抛出的异常通常是TypeError(您使用了无效操作)和AttributeError(您尝试访问不存在的成员(方法也是成员))。


7
投票

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

大多数Python代码属于所谓的"Duck Typing" - 例如,你在一个对象上寻找一个方法read - 你不关心对象是磁盘上的文件还是套接字,你只想读取N个字节来自它。


5
投票

作为Alex Martelli explains

正常的,Pythonic,首选解决方案几乎总是“鸭子打字”:尝试使用该参数,就好像它是某种所需的类型一样,在try / except语句中执行它,捕获所有异常,如果参数实际上不是那种类型(或任何其他类型很好地模仿它;-),并在except子句中,尝试其他东西(使用参数“好像”它是其他类型)。

阅读他的帖子的其余部分以获取有用的信息。


3
投票

Python并不关心你传递给它的函数。当你调用my_func(a,b)时,param1和param2变量将保存a和b的值。 Python不知道你用正确的类型调用函数,并期望程序员处理它。如果使用不同类型的参数调用函数,则可以使用try / except块包装访问它们的代码,并以任何方式评估参数。


2
投票

你从不指定类型; Python有duck typing的概念;基本上处理参数的代码将对它们做出某些假设 - 可能通过调用参数预期实现的某些方法。如果参数类型错误,则抛出异常。

通常,您的代码可以确保您传递正确类型的对象 - 没有编译器可以提前强制执行此操作。


2
投票

有一个臭名昭着的例外,在这个页面上值得一提的鸭子打字。

str函数调用__str__类方法时,它巧妙地сhecks其类型:

>>> 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暗示我们如果遇到意外类型,程序会引发哪个异常。

© www.soinside.com 2019 - 2024. All rights reserved.