19

我打算使用Python 函数注释来指定静态工厂方法的返回值的类型。我知道这是注释的理想用例之一。

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

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

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

4

2 回答 2

12

Trie是一个有效的表达式,并计算为与名称 name 关联的当前值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())并将其解释给实际处理注释的任何内容。

于 2013-04-01T10:48:58.767 回答
11

PEP 484 以前向引用的形式为此提供了官方解决方案。

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

在问题代码的情况下:

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
于 2016-05-22T11:25:35.943 回答