问题描述
如何使用基类方法访问派生类的属性(其他语言中的静态变量)? 即
class Base:
@classmethod
def get_list(cls):
return [1,2,cls.x]
class Derived(Base):
x = 3
foo = Base.get_list()
Derived.foo # Hope to have it set to [1,3]
我不希望直接将x传递给基类,即foo = Base.get_list(x)
,因为我有很多用相同变量调用的方法。
编辑
评论要求上下文。我试图提供一个最低限度的可重现性示例,以使大家省去细节。
这里是完整的上下文:因此,它实际上与Django Admin有关。 我有一个抽象模型。然后,我具有用于抽象模型的各种具体实现的各种管理界面。如您所料,我创建了一个抽象的管理模型来处理常见的逻辑。处理常见管理逻辑的一部分,例如在派生的管理模型上生成属性。
class FactAdmin(admin.ModelAdmin):
@classmethod
def build_list_display(cls,*custom):
return (
'created_utc',*custom,'foo__bar__abbreviation','foo__baz__name','foo__caz__name','foo__daz__description','foo__eaz__code',)
@admin.register(FooEntry)
class FoopEntryAdmin(FactAdmin):
fieldsets = FactAdmin.build_list_display('cannon_ball','pogo_stick')
因此,在基本admin类中,它不断重用自定义字段,自定义模型和其他内容的列表。因此,与其每次都为每个属性传递它们,不如将其设置为静态属性比较干燥。因此,我希望将它们设置为派生类的静态成员字段,并在基类中访问它们。
解决方法
您不应使用类属性和方法。您应该定义用于创建实例的类,并使用实例方法和属性,以使常规继承能够按预期的方式在语言中使用。
然后,您需要利用实例方法可以被子类覆盖的事实。在这里,为了让父类到达子类的x
,可以在父类中定义一个您希望在子类中被覆盖的get_x
方法。这样子类的x
被父类使用,而不会传递给子类。
这是一个例子:
class Base:
def set_foo(self):
return [1,2,self.get_x()]
def get_x(self):
return 0
class Sub(Base):
def __init__(self):
self.x = 3
self.foo = super().set_foo()
def get_x(self):
return self.x
print(Sub().foo)
结果:
[1,3]
,
在子类上调用的超类类方法中访问子类属性不是问题。可以按预期工作:
class Base:
@classmethod
def get_list(cls):
return [1,cls.x]
class Derived(Base):
x = 3
assert Derived.get_list() == [1,3]
但是,您不能在子类的类主体中轻松地调用该类方法,因为当时Derived
的类对象和名称Derived
尚不存在。 (您可以在方法,类方法和静态方法中调用它,因为它们的主体是在调用它们时进行评估的,而不是在定义它们时进行评估的。但这在这里无济于事。)并且如果在Base
上调用显然缺少x
。 (而且,作为get_list
的Python类方法,必须在某些对象上显式调用。我们不能像Java中那样进行不合格的调用。)
所以
class Derived(Base):
x = 3
foo = get_list()
得出NameError: name 'get_list' is not defined
,
class Derived(Base):
x = 3
foo = Base.get_list()
得出AttributeError: type object 'Base' has no attribute 'x'
和
class Derived(Base):
x = 3
foo = Derived.get_list()
得出NameError: name 'Derived' is not defined
。
但是一切并没有丢失。别忘了Python允许我们从类外部创建和设置类属性。如此有效:
class Base:
@classmethod
def get_list(cls):
return [1,cls.x]
class Derived(Base):
x = 3
Derived.foo = Derived.get_list()
assert Derived.foo == [1,3]
,
如果您希望super访问派生类的属性,则可能是因为此属性被所有派生子类共享(无论如何您都无需对派生类类型进行任何检查)。在这种情况下,最合乎逻辑的事情是改为在super中实现该属性,从而解决了问题:
class Base:
x = 3
@classmethod
def get_foo():
return [1,self.x]