Python 函数如何处理您传入的参数类型?

新手上路,请多包涵

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

 def my_func(param1, param2):
    # stuff

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

原文由 Leif Andersen 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 427
2 个回答

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

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

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

原文由 Alex Martelli 发布,翻译遵循 CC BY-SA 3.0 许可协议

其他答案在解释 duck typing 和 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 的类型,这在语法上是允许的,但它不利于以编程方式解析(我们稍后会回过头来)。

重要的是要注意,如果您将浮点数传递给 --- ,Python 不会引发 TypeError index ,原因是 Python 设计理念的要点之一: “我们这里都是同意的成年人” ,这意味着您应该知道什么可以传递给函数,什么不能传递给函数。如果你真的想编写抛出 TypeErrors 的代码,你可以使用 isinstance 函数来检查传递的参数是正确的类型还是它的子类,如下所示:

 def pick(l: list, index: int = 0) -> int:
    if not isinstance(l, list):
        raise TypeError
    return l[index]

关于为什么你应该很少这样做以及你应该做什么的更多信息将在下一节和评论中讨论。

PEP 3107 不仅提高了代码的可读性,而且还有几个合适的用例,您可以在 此处 阅读。


随着 PEP 484 的引入,类型注释在 Python 3.5 中得到了更多的关注, PEP 484 引入了一个标准模块 typing 用于类型提示。

这些类型提示来自类型检查器 mypy ( GitHub ),它现在符合 PEP 484 标准。

typing 模块带有相当全面的类型提示集合,包括:

  • List , Tuple , Set , Dict - for list , tuple , setdict 分别。
  • Iterable - 对发电机有用。
  • Any - 什么时候都可以。
  • Union - 当它可以是指定类型集中的任何内容时,而不是 Any
  • Optional - 当它 可能 是无。 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 代码时,可以通过编写如下格式的文档字符串来获得上述某些功能:

 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 有史以来最伟大的特性之一。我迫不及待地希望人们开始利用它的力量。很抱歉回答很长,但这就是我兴奋时会发生的事情。


可以在 此处 找到大量使用类型提示的 Python 代码示例。

原文由 erb 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题