在派生类中访问基类属性 - 在“类范围”中

Posted

技术标签:

【中文标题】在派生类中访问基类属性 - 在“类范围”中【英文标题】:Access base class attribute in derived class - in "class scope" 【发布时间】:2015-04-28 09:14:55 【问题描述】:
class Outer(object):
    class InnerBase(object): _var = 'foo', 'bar'
    class Derived(InnerBase):
        _var = _var | 'baz' # NameError: name '_var' is not defined
        _var = InnerBase._var | 'baz' #  name 'InnerBase' is not defined
        _var = Outer.InnerBase._var | 'baz' #  free variable 'Outer'
        # referenced before assignment in enclosing scope

Outer 中移动 _var 没有帮助 - 在模块范围内移动它会起作用,但会破坏拥有类的目的。那么该怎么做呢?

编辑:来自 Java,所以类的范围规则对我来说是一个令人头疼的问题 - 简报将不胜感激。顺便说一句,这有效:

    class Derived(InnerBase): pass
    Derived._var = InnerBase._var | 'baz'

但这不是优雅的顶峰。

相关:Nested classes' scope? - 但在这里我们特别想访问我们的父类(而不是 Outer 类型)

EDIT2:我真正追求的是类似_var = __class__._var 的语法(或hack),或者解释为什么它不存在

【问题讨论】:

【参考方案1】:

Python 从不在封闭类语句中搜索名称。 Mark Lutz 在他的 Python 简介(Learning Python)中使用首字母缩略词 LEGB 来概括范围:Python 搜索本地范围,然后搜索任何封闭的 def 语句的本地范围,然后搜索全局范围,最后是内置范围。类语句不包括在此范围列表中; Python 不会在封闭的类语句中搜索名称。

一种解决方案是取消嵌套类。在 Python 中,由于其简单性,通常首选使用非嵌套类。但是,当然,嵌套类也有充分的理由。为什么要嵌套InnerBase?我想知道您是否可能因为您在 Java 方面的经验而嵌套了该类。以下内容也适合您吗?

class InnerBase(object):
    _var = 'foo', 'bar'

class Derived(InnerBase):
    _var = InnerBase._var | 'baz'

>>> Derived._var
set(['baz', 'foo', 'bar'])

当您将这两个类语句嵌套在另一个类语句下时,它们将被排除在名称搜索之外,因为它们已成为更大的类语句的一部分,因此被排除在各种范围的搜索之外。

【讨论】:

没有那些嵌套类自然适合那里(我实际上正在重构现有设计 - 我打算根除这些类,所以这又是一个好奇的问题)。我们能否以某种方式使用我们 (Derived) 实际上已经拥有 _var 的事实? (如果只有_var = __class__._var 会编译...) Derived 没有_var。它的基类可以,并且Derived._var 可以工作,因为继承的属性查找是如何工作的。但是,LEGB 规则说 Derived 实际上在类定义完成之前并不是访问其基类的方法(至少,在使用 class 语句时)。 接受了 chepner 对“hack”部分的回答(相信我,我已经搜索过)——但你的回答在“类范围规则”部分提供了更多的见解。随意添加指向 LEGB 首字母缩写词的链接 :)【参考方案2】:

您可以绕过class 语句并使用对type 的显式调用。

class Outer(object):
    class InnerBase(object): _var = 'foo', 'bar'
    Derived = type('Derived',
                   (InnerBase,),
                   '_var': InnerBase._var | 'baz'
                  )

这是因为Derived._var 不是通过定义Derivedclass 语句中的赋值语句设置的,而是从您在与InnerBase 本身相同的环境中创建的字典中导入的。

【讨论】:

【参考方案3】:

当你把它放在一个方法中时它会起作用。

    def __init__(self):
        _var = Outer.InnerBase._var | 'baz'
        #_var = super(Outer.Derived, self)._var | 'baz'    # or with super
        print(_var)

所以我的印象是,这都是关于初始化的。

【讨论】:

离题 - 我想在类范围内访问它 - 不是每次初始化时都访问一次 我觉得它不是那么离题,你总是可以使用 staticmethod 或 classmethod。 静态方法和类方法会以同样的方式进行 - 不要个人认为,在这个社区中,如果没有完全工作,习惯上会删除一个人的答案;)

以上是关于在派生类中访问基类属性 - 在“类范围”中的主要内容,如果未能解决你的问题,请参考以下文章

在派生类中访问基类属性 - 在“类范围”中

C++中派生类的构造函数怎么显式调用基类构造函数?

C ++中派生类的相等性测试[重复]

c++解决二义性的方法

C++ 命名空间中派生类中的 Qt 样式表(选择器)

当我在 C++ 中派生一个类时,它是不是会创建一个基类对象并将其作为我的成员变量存储在派生类中?