我有一个具有类属性my_list
的类Klass
。我有它的一个子类SubKlass
,我想在其中有一个类属性my_list
,它是父类中相同属性的修改版本:
class Klass():
my_list = [1, 2, 3]
class SubKlass(Klass):
my_list = Klass.my_list + [4, 5] # this works, but i must specify parent class explicitly
#my_list = super().my_list + [4, 5] # SystemError: super(): __class__ cell not found
#my_list = my_list + [4, 5] # NameError: name 'my_list' is not defined
print(Klass.my_list)
print(SubKlass.my_list)
那么,有没有一种方法可以访问父类属性而不指定其名称?
更新:
Python问题跟踪器上有一个错误:http://bugs.python.org/issue11339。让我们希望它能在某个时候得到解决。
你不能。
类定义在Python中的工作原理如下。
-
解释器看到一个
class
语句,后面跟着一个代码块。 -
它创建一个新的名称空间,并在名称空间中执行该代码。
-
它调用带有结果名称空间、类名、基类和元类(如果适用)的
type
内建。 -
它将结果分配给类的名称。
在类定义中运行代码时,您不知道基类是什么,因此无法获取它们的属性。
可以做的是在定义类后立即修改它。
EDIT:这里有一个可以用来更新属性的小类装饰器。这个想法是你给它一个名字和一个函数。它会查看类的所有基类,并使用该名称获取它们的属性。然后,它使用从基类继承的值列表和您在子类中定义的值来调用函数。此调用的结果绑定到名称。
代码可能更有意义:
>>> def inherit_attribute(name, f):
... def decorator(cls):
... old_value = getattr(cls, name)
... new_value = f([getattr(base, name) for base in cls.__bases__], old_value)
... setattr(cls, name, new_value)
... return cls
... return decorator
...
>>> def update_x(base_values, my_value):
... return sum(base_values + [my_value], tuple())
...
>>> class Foo: x = (1,)
...
>>> @inherit_attribute('x', update_x)
... class Bar(Foo): x = (2,)
...
>>> Bar.x
(1, 2)
其思想是在Bar
中将x
定义为(2,)
。然后,decorator将遍历Bar
的子类,找到它们的所有x
,并用它们调用update_x
。所以它会调用
update_x([(1,)], (2,))
它通过连接它们来组合它们,然后再次将它们绑定回x
。这有道理吗?
正如@katrielalex所回答的,在创建类之前,默认情况下my_list
不在命名空间中。然而,在Python3中,如果您想深入研究元类,您可以手动将my_list
添加到命名空间中:
class Meta(type):
def __prepare__(name, bases, **kwds):
print("preparing {}".format(name), kwds)
my_list = []
for b in bases:
if hasattr(b, 'my_list'):
my_list.extend(b.my_list)
return {'my_list': my_list}
class A(metaclass=Meta):
my_list = ["a"]
class B(A):
my_list.append(2)
print(A().my_list)
print(B().my_list)
请注意,这个例子可能还不能很好地处理菱形继承结构。
新的__prepare__
属性在PEP 3115中定义。
您可以使用元类或类装饰器。以下是如何在Python2.x中使用元类:
class Klass(object):
my_list = [1, 2, 3]
class KlassMeta(type):
def __new__(cls, name, bases, attrs):
# grab last base class, which in this case is Klass
attrs['my_list'] = bases[-1].my_list + [4, 5]
return super(KlassMeta, cls).__new__(cls, name, bases, attrs)
class SubKlass(Klass):
__metaclass__ = KlassMeta
使用Python3,您可以使用更新的语法声明元类:
class SubKlass(Klass, metaclass=KlassMeta):
pass
没有办法离开类的主体;从self可用的上下文(或类本身)中,可以检查__bases__
。
正如你从其他回复中看到的,比如katriealex的:不,你不能。至少不容易。
Q: 你到底想做什么?
Q: 你为什么要这么做?