16

我不完全确定这是针对stackoverflow的,所以如果不是,请纠正我。

即说我们有 t.py 内容:

class A(object):
    pass
print("A:", A)

class B(object):
    print("From B: A:", A)


class OuterClass(object):
    class AA(object):
        pass
    print("AA:", AA)

    class BB(object):
        print("From BB: AA:", AA)

现在我们执行它:$ python3 t.py

A: <class '__main__.A'>
From B: A: <class '__main__.A'>
AA: <class '__main__.AA'>
From BB: AA:
Traceback (most recent call last):
  File "t.py", line 9, in <module>
    class OuterClass(object):
  File "t.py", line 14, in OuterClass
    class BB(object):
  File "t.py", line 15, in BB
    print "From BB: AA:", AA
NameError: name 'AA' is not defined

从文档:

类定义是一个可执行语句。它首先评估继承列表(如果存在)。继承列表中的每个项目都应评估为允许子类化的类对象或类类型。然后使用新创建的本地名称空间和原始的全局名称空间在新的执行框架中执行该类的套件(请参阅命名和绑定部分)。(通常,套件只包含函数定义。)当类的套件完成执行时,它的执行帧被丢弃,但它的本地命名空间被保存。[4] 然后使用基类的继承列表和属性字典的保存的本地名称空间创建一个类对象。类名绑定到原始本地命名空间中的此类对象。

所以我理解这种行为,但不理解使范围不像其他地方那样具有词汇性的理由。这违背了“特殊情况不足以打破规则”。为什么class行为不同于,说,def

这是一个“实用胜于纯粹”的案例吗?如果是这样,理由是什么?我最初认为它可能是 python 2.x 的产物,但正如您在上面看到的,这种行为也存在于 python 3.3 中。

4

1 回答 1

11

正如 Wooble 在评论中指出的那样,类块确实创建了一个新范围。问题是嵌套在该范围内的范围无法访问类块范围中的名称。文档中提到了这一点:

类块中定义的名称范围仅限于类块;它没有扩展到方法的代码块——这包括理解和生成器表达式,因为它们是使用函数范围实现的。

我现在找不到它的来源,但是在某个地方(我认为是在 StackOverflow 问题上)我发现了一个合理的理由:如果类 def 可以在嵌套块内访问,则方法名称会影响全局函数,包括内置函数。这会使创建具有简短名称的方法但也使用同名的内置函数的类变得很尴尬。例如,您不能这样做:

class SomeDataStructure(object):
    def sum(self):
        return sum(self._data)
    def otherCalc(self):
        return sum(item for item in somethingElse)

如果类块在方法的范围内,这将导致无限递归,因为内部sum调用将有权访问方法名称并将调用该方法而不是内置sum函数。同样,其他方法(例如otherCalc方法)将不再有权访问全局 sum 函数以供自己使用,但始终会获取方法。 对另一个 SO question 的回答通过说“这是因为您应该使用selfPython 中的方法来访问”来描述这一点。

现在,该参数实际上只对类内部的函数有意义,因为函数体在 is 时不执行,但在语句 isdef执行类体。但是,我认为如果你将我上面所说的与这篇博文中的概念结合起来,你就会得到一个合理的理由。也就是说,嵌套类没有——现在仍然没有——被认为是一种值得支持的风格。但是,类中的函数当然是司空见惯的。处理类内函数用例的最简单方法是使块不将其名称传递给任何classclass嵌套范围。如果将其名称传递给嵌套类而不是嵌套函数可能会更好,但这将是一个更复杂的规则,并且没有人足够关心支持嵌套类以使其值得。

于 2012-12-18T19:21:22.053 回答