可以从嵌套类定义中实现继承吗?

3

让我感到惊讶的是,这段代码会产生以下错误(Python 2.7.3):

class Foo(object):
    class Bar(object):
        pass
    class Baz(object):
        class InnerBar(Bar):
            pass

导致结果:

Enthought Python Distribution -- www.enthought.com
Version: 7.3-2 (64-bit)

Python 2.7.3 |EPD 7.3-2 (64-bit)| (default, Apr 11 2012, 17:52:16)
[GCC 4.1.2 20080704 (Red Hat 4.1.2-44)] on linux2
Type "credits", "demo" or "enthought" for more information.
Hello
>>> class Foo(object):
...     class Bar(object):
...         pass
...     class Baz(object):
...         class InnerBar(Bar):
...             pass
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in Foo
  File "<stdin>", line 5, in Baz
NameError: name 'Bar' is not defined

我的怀疑是这与等价的Bar = type(...) class赋值的特定执行有关,但即便如此,为什么Bar不能在Baz内部通过闭包得到呢?

有没有办法解决这个精确实现的问题(也就是说,我不想放宽Bar是在Foo内定义的普通class的限制,而且其他嵌套在Foo中的类可能也想从Bar继承)。

我考虑的具体用例是模拟一些数据库结构,以实现更符合面向对象设计的方案,以反映数据服务项目中的模式。大部分代码都是由针对XSD执行检查的程序生成的Python代码,所以在类中定义其他类是模拟XSD复杂类型的某些方式的自然方法,但同时还能够表明它们何时是其他复杂类型的子级等。这就是我不愿意将类拉出Foo供后续继承的原因。

2个回答

3
这里没有闭包。当执行嵌套代码时,类的主体不是父作用域。
就嵌套作用域中的代码而言,只存在全局和本地作用域,Foo类主体不是可访问的命名空间。
参见命名和绑定文档

如果定义出现在函数块中,则作用域扩展到定义块内包含的任何块,除非包含块为名称引入了不同的绑定。在类块中定义的名称的作用域限于类块

重点在我身上。
这里的解决方法是使用嵌套函数来生成可以访问的作用域:
class Foo(object):
    def produce_nested_scope():
        class Bar(object):
            pass
        class Baz(object):
            class InnerBar(Bar):
                pass
        return Bar, Baz
    Bar, Baz = produce_nested_scope()
    del produce_nested_scope

演示:

>>> class Foo(object):
...     def produce_nested_scope():
...         class Bar(object):
...             pass
...         class Baz(object):
...             class InnerBar(Bar):
...                 pass
...         return Bar, Baz
...     Bar, Baz = produce_nested_scope()
...     del produce_nested_scope
... 
>>> Foo.Bar
<class '__main__.Bar'>
>>> Foo.Baz
<class '__main__.Baz'>

我仍然非常不确定为什么文档化的过程在Foo正在进行的本地命名空间中对BarBaz没有完全发生。在Foo成为绑定名称之前正在为Foo开发的任何本地名称空间中,为什么不会完成所有Bar的套件,然后将Bar绑定到命名空间中,然后执行所有Baz的套件(包括InnerBar,它可以查找一个命名空间级别以找到Bar,因为Bar将已经绑定在命名空间中),然后Foo完成并绑定?我错过了什么吗? - ely
@EMS:啊,今天我缺乏咖啡因。我以为你在Foo内部使用了Foo。我看错了。不,类命名空间并不是查找其他名称的作用域。无法找到Bar,因为Foo套件不是父级作用域。 - Martijn Pieters

1
这是一个可怕的hack:
class Foo(object):
    def hack(dest):
        class Bar(object):
            pass
        class Baz(object):
            class InnerBar(Bar):
                pass

        dest.update(locals())
    hack(locals())

>>> Foo.Bar
<class '__main__.Bar'>
>>> Foo.Baz.InnerBar
<class '__main__.InnerBar'>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接