【问题标题】:typing.Any vs object?打字。任何与对象?
【发布时间】:2017-02-10 12:58:41
【问题描述】:

在打字时使用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 python-3.5 type-hinting python-typing


    【解决方案1】:

    是的,有区别。尽管在 Python 3 中,所有对象都是 object 的实例,包括 object 本身,但只有 Any 记录了类型检查器应忽略返回值。

    Any 类型的文档字符串表明 object 是 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时,类型检查器将拒绝对其进行的几乎所有操作,并将其分配给更专业类型的变量(或将其用作返回值)是一种类型错误。

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

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

    object 可以是 cast 到更具体的类型,而 Any 真正意味着 任何事情都发生 并且类型检查器会脱离对对象的任何使用(即使您稍后分配这样的经过类型检查的名称的对象)。

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

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

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

    【讨论】:

    • 这很有趣,object/type 结构的另一种特殊情况。再次感谢马丁! :)
    • 不幸的是,这并不完全正确——请参阅下面的答案。特别是,关键是Any 是完全不受约束的——允许对Any 类型的值进行任何操作。相比之下,object 是最受约束的类型。如果您有一个Any 类型的值,那么您唯一可以执行的操作是属于object 接口的一部分(例如__str__ 等)。 “对象是 Any 的子类,反之亦然”主要是为了解释为什么所有值都与 Any 兼容,即使它们在技术上不是该类型的子类或超类。
    • @Michael0x2a: 对,所以从打字的角度来看,Any“允许”该函数使用object.__missing__,而object 不会,因为该方法是可选的。这样Any 记录了函数将如何使用参数,而不仅仅是干巴巴地应用 isinstance 测试。实际上,两者保持不变,因为typing 中使用的isinstance() 测试将通过任一方式。
    • @MartijnPieters:我不确定我是否完全同意。根据经验,使用 Any 而非 object 最终会产生重大影响,因为它会削弱代码的类型安全性。例如。将Any 类型的值传递给期望一些CustomType 的函数是合法的,即使这样做最终会导致运行时错误,因为实际上任何操作都可以使用Anyobject 并非如此。我还认为object 更准确地捕捉到了意图,因为它准确地概述了在值上使用什么接口是安全的,而不是让它模棱两可。
    • @Michael0x2a:啊,我明白你的意思,更新了我的答案以反映这一点。我只是在这里查看typing 的实现。
    【解决方案2】:

    Anyobject 表面上相似,但实际上完全相反

    object 是 Python 元类层次结构的。每个类都继承自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] 类型,这比什么都没有好一点。

    相比之下,如果您想以类型安全的方式指示值必须与任何可能存在的对象一起工作,请使用 object

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

    有关详细信息,请参阅:


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

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

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

    【讨论】:

    • 不会List[T] 将列表限制为同质的,例如[None, 1, 'foo'] 是非法的,因为该列表中没有 one 类型?使用list作为接受类型,宝宝已经和洗澡水一起被扔掉了;没有对包含的值设置任何约束。
    • @MartijnPieters -- 不一定 -- T 可以被限制为 AnyUnion[None, int, str]。这两种选择都会使您成为[None, 1, 'foo'] 类型检查的示例。但是,如您所说,将类型设置为list 相当于执行List[Any]
    • 对,列表来源的声明elsewhere应该正确定义列表内容。我同意在这里实际限制要好得多; Any 会在后续使用 get_item() 的返回值时关闭类型检查器(您可以将返回值分配给先前受约束的变量,它会正常工作,但后果自负)。
    猜你喜欢
    • 2013-09-28
    • 1970-01-01
    • 2021-09-17
    • 2021-06-29
    • 1970-01-01
    • 2022-11-18
    • 2022-10-16
    • 1970-01-01
    相关资源
    最近更新 更多