typing.Any vs 对象?

typing.Any vs object?

在打字时使用 typing.Anyobject 有什么区别吗?例如:

def get_item(L: list, i: int) -> typing.Any:
    return L[i]

相比于:

def get_item(L: list, i: int) -> object:
    return L[i]

是的,有区别。尽管在 Python 3 中,所有对象都是 object 的实例,包括 object 本身,但只有 Any 文档表明类型检查器应忽略 return 值。

Any 类型文档字符串声明对象是 Any 的子类,反之亦然:

>>> import typing
>>> print(typing.Any.__doc__)
Special type indicating an unconstrained type.

    - Any object is an instance of Any.
    - Any class is a subclass of Any.
    - As a special case, Any and object are subclasses of each other.

然而,一个适当的类型检查器(一个超越 isinstance() 检查的类型检查器,它检查对象在函数中的实际 使用)可以很容易地反对 object 其中 Any 始终被接受。

来自Any type documentation:

Notice that no typechecking is performed when assigning a value of type Any to a more precise type.

Contrast the behavior of Any with the behavior of object. Similar to Any, every type is a subtype of object. However, unlike Any, the reverse is not true: object is not a subtype of every other type.

That means when the type of a value is object, a type checker will reject almost all operations on it, and assigning it to a variable (or using it as a return value) of a more specialized type is a type error.

来自 mypy 文档部分 Any vs. object:

The type object is another type that can have an instance of arbitrary type as a value. Unlike Any, object is an ordinary static type (it is similar to Object in Java), and only operations valid for all types are accepted for object values.

object 可以 cast 到更具体的类型,而 Any 实际上意味着 任何事情都发生 并且类型检查器脱离任何使用该对象(即使您稍后将此类对象分配给一个 类型检查的名称)。

您已经通过接受 list 将您的函数绘制到一个未键入的角落,归结为与 List[Any] 相同的东西。类型检查器 在那里脱离了 并且 return 值不再重要,但是由于您的函数接受包含 Any 个对象的列表,因此正确的 return 值将在这里 Any

要正确参与类型检查代码,您需要将输入标记为 List[T](通用类型容器),以便类型检查器能够关心 return 值。在你的情况下,这将是 T 因为你正在从列表中检索一个值。从 TypeVar:

创建 T
from typing import TypeVar, List

T = TypeVar('T')

def get_item(L: List[T], i: int) -> T:
    return L[i]

Anyobject表面上很像,实际上完全相反

object 是 Python 的元 class 层次结构的 root。每一个 class 都继承自 object。这意味着 object 在某种意义上是您可以赋予值的最具限制性的类型。如果你有一个 object 类型的值,你唯一可以调用的方法是属于每个对象的方法。例如:

foo = 3  # type: object

# Error, not all objects have a method 'hello'
bar = foo.hello()   

# OK, all objects have a __str__ method
print(str(foo))   

相比之下,Any 是一个 逃生口 旨在允许您将动态和静态类型的代码混合在一起。 Any 是限制最少的类型——任何可能的方法或操作都允许对类型 Any 的值进行。例如:

from typing import Any
foo = 3  # type: Any

# OK, foo could be any type, and that type might have a 'hello' method
# Since we have no idea what hello() is, `bar` will also have a type of Any
bar = foo.hello()

# Ok, for similar reasons
print(str(foo))

您通常应该尝试使用 Any 仅用于以下情况...

  1. 作为一种混合动态和静态类型代码的方法。例如,如果您有许多动态和复杂的函数,并且没有时间完全静态地键入所有这些函数,您可以满足于只给它们一个 return 类型的 Any 名义上将它们带入类型检查工作. (或者换句话说,Any 是一个有用的工具,可以帮助分阶段将未经类型检查的代码库迁移到类型化的代码库)。
  2. 作为一种为难以键入的表达式指定类型的方法。例如,Python 的类型注释目前不支持递归类型,这使得输入诸如任意 JSON 指令之类的东西变得困难。作为一个临时措施,你可能想给你的 JSON 指令一个 Dict[str, Any] 的类型,这比没有好一点。

相比之下,使用 object 来表示您希望以类型安全的方式表明值必须字面上与任何可能存在的对象一起工作。

我的建议是避免使用 Any,除非别无选择。 Any 是一种让步——一种允许我们真正希望生活在类型安全世界中的活力的机制。

有关详细信息,请参阅:


对于您的特定示例,我会使用 TypeVars,而不是对象或 Any。您想要做的是表明您想要 return 列表中包含的任何内容的类型。如果列表将始终包含相同的类型(通常是这种情况),您会想要这样做:

from typing import List, TypeVar

T = TypeVar('T')
def get_item(L: List[T], i: int) -> T:
    return L[i]

这样,您的 get_item 函数将 return 尽可能精确的类型。