为啥包含类的名称不被识别为返回值函数注释? [复制]

Posted

技术标签:

【中文标题】为啥包含类的名称不被识别为返回值函数注释? [复制]【英文标题】:Why is the name of the containing class not recognized as a return value function annotation? [duplicate]为什么包含类的名称不被识别为返回值函数注释? [复制] 【发布时间】:2013-03-22 10:27:12 【问题描述】:

我打算使用Python function annotations 来指定静态工厂方法的返回值的类型。我知道这是one of the desired use cases 用于注释。

class Trie:
    @staticmethod
    def from_mapping(mapping) -> Trie:
        # docstrings and initialization ommitted
        trie = Trie()
        return trie

PEP 3107 声明:

函数注解只不过是在编译时将任意 Python 表达式与函数的各个部分相关联的一种方式。

Trie 在 Python 中是一个有效的表达式,不是吗? Python 不同意,或者更确切地说,找不到名称:

def from_mapping(mapping) -> Trie:NameError: name 'Trie' is not defined

值得注意的是,如果指定了基本类型(如objectint)或标准库类型(如collections.deque),则不会发生此错误。

是什么导致了这个错误,我该如何解决?

【问题讨论】:

【参考方案1】:

Trie 是一个有效的表达式,计算结果为与名称名称 Trie 关联的当前值。但该名称尚未定义——类对象仅在类主体运行到完成之后绑定到其名称。在这个更简单的示例中,您会注意到相同的行为:

class C:
    myself = C
    # or even just
    C

通常,解决方法是在定义类之后在类主体之外设置类属性。这在这里不是一个很好的选择,尽管它有效。或者,您可以在初始定义中使用任何占位符值,然后在 __annotations__ 中替换它(这是合法的,因为它是常规字典):

class C:
    def f() -> ...: pass
print(C.f.__annotations__)
C.f.__annotations__['return'] = C
print(C.f.__annotations__)

确实感觉相当hacky。根据您的用例,可能可以改用哨兵对象(例如CONTAINING_CLASS = object())并将其解释给实际处理注释的任何内容。

【讨论】:

有趣...但是,我可以在我的静态方法中使用类名inside。这是因为函数的内容只在执行时才被解析,而签名是在类定义之后立即解析的?这是 Python 语言参考的the relevant part,供其他感兴趣的人参考。 @codesparkle:解析与执行是分开的。错误发生在执行时。您的类定义(包括注释)是代码的执行。但是,在定义类时(显然)不会执行函数的内部结构。 -> 'returns Trie'怎么样 @jamylak 根据用例,这是可能的。但是,在许多用例中,它的用处不大,甚至很糟糕(即任何希望程序确定您正在谈论的类型的情况)。老实说,对于那个文字描述,我只使用一个文档字符串。【参考方案2】:

PEP 484 以forward references 的形式为此提供了官方解决方案。

当类型提示包含尚未定义的名称时,该定义可以表示为字符串文字,稍后再解析。

问题码的情况下:

class Trie:
    @staticmethod
    def from_mapping(mapping) -> Trie:
        # docstrings and initialization ommitted
        trie = Trie()
        return trie

变成:

class Trie:
    @staticmethod
    def from_mapping(mapping) -> 'Trie':
        # docstrings and initialization ommitted
        trie = Trie()
        return trie

【讨论】:

哇,我很惊讶我错过了 - 感谢您提供这个答案。

以上是关于为啥包含类的名称不被识别为返回值函数注释? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

有些日期被识别为日期,有些日期不被识别。为啥?

术语“go”不被识别为cmdlet,函数,脚本文件或可操作程序的名称

术语'cmake'不被识别为cmdlet的名称

在带有任务计划程序信息的提示符下运行 powershell 时出错 - “不被识别为 cmdlet、函数、脚本文件或可操作的名称..”

为啥我会收到此错误:当我使用 tailwindcss 时,'postcss' 不被识别为内部或外部命令、可运行程序或批处理文件

派生类不被识别为协变