在派生类中访问基类属性 - 在“类范围”中
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
不是通过定义Derived
的class
语句中的赋值语句设置的,而是从您在与InnerBase
本身相同的环境中创建的字典中导入的。
【讨论】:
【参考方案3】:当你把它放在一个方法中时它会起作用。
def __init__(self):
_var = Outer.InnerBase._var | 'baz'
#_var = super(Outer.Derived, self)._var | 'baz' # or with super
print(_var)
所以我的印象是,这都是关于初始化的。
【讨论】:
离题 - 我想在类范围内访问它 - 不是每次初始化时都访问一次 我觉得它不是那么离题,你总是可以使用 staticmethod 或 classmethod。 静态方法和类方法会以同样的方式进行 - 不要个人认为,在这个社区中,如果没有完全工作,习惯上会删除一个人的答案;)以上是关于在派生类中访问基类属性 - 在“类范围”中的主要内容,如果未能解决你的问题,请参考以下文章