打字。任何vs对象?

时间:2016-10-02 12:38:35

标签: python python-3.5 type-hinting

在输入时使用typing.Any而不是object之间有什么区别吗?例如:

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

与:相比:

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

2 个答案:

答案 0 :(得分:19)

是的,有区别。虽然在Python 3中,所有对象都是object的实例,包括object本身,但只有Any文件表明返回值应该被类型检查器忽略。

Any类型docstring声明该对象是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

  

请注意,在将类型Any的值分配给更精确的类型时,不会执行类型检查。

  

Any的行为与object的行为进行对比。与Any类似,每种类型都是object的子类型。但是,与Any不同,反之则不然:object不是所有其他类型的子类型。

     

这意味着当值的类型为object时,类型检查器将拒绝几乎所有操作,并将其分配给更专业类型的变量(或将其用作返回值)类型错误。

并从mypy文档部分Any vs. object

  

类型object是另一种类型,可以将任意类型的实例作为值。与Any不同,object是普通的静态类型(在Java中类似于Object),并且只接受对所有类型有效的操作。

object可以cast为更具体的类型,而Any实际上意味着任何内容,并且类型检查器不会对任何对象的使用脱离(即使您稍后将此类对象分配给 typechecked的名称。)

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

要正确参与类型检查代码,您需要将输入标记为List[T](通用类型的容器),以便为类型检查器关注返回值。在您的情况下,由于您从列表中检索值,因此T。从T

创建TypeVar
from typing import TypeVar, List

T = TypeVar('T')

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

答案 1 :(得分:6)

Anyobject表面上相似,但事实上其意义完全相反

object是Python的元类层次结构的 root 。每个类都继承自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. 将动态和静态类型代码混合在一起的方法。例如,如果你有许多动态和复杂的功能,并且没有时间完全静态地键入所有这些功能,那么你可以满足于只给它们一个返回类型的Any来名义上将它们带入类似的工作中。 (或者换句话说,Any是一个有用的工具,可以帮助将未经验证的代码库分阶段迁移到类型化的代码库。)
  2. 作为一种给难以输入的表达式赋予类型的方法。例如,Python的类型注释目前不支持递归类型,这使得输入像任意JSON dicts这样的东西很困难。作为一种临时措施,您可能希望为JSON dicts提供Dict[str, Any]类型,这比没有任何东西好一点。
  3. 相比之下,如果您希望以类型安全的方式指示值必须与现有的任何可能对象一起使用,则使用object

    我的建议是避免使用Any,除非没有其他选择。 Any是一种让步 - 一种允许我们真正宁愿生活在类型安全世界中的动力的机制。

    有关详细信息,请参阅:

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

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

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