如何在 super class 方法中访问 subcclass 属性,而不传入它们
How to access subcclass attributes in super class methods, without passing them in
如何在其基本 classes 方法中访问派生的 classes 属性(其他语言中的静态变量)?
即
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, 2, 3]
我不希望将 x 直接传递给基数 class,即 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')
因此在基础管理 class 中,它不断重复使用自定义字段、自定义模型和其他内容的列表。因此,与其每次都传递它们,对于每个 属性,将它们设置为静态属性似乎更 DRY。因此我希望将它们设置为派生 class 上的静态成员字段,并在基础 class.
中访问它们
如果你想让 super 访问派生 class 的 属性,可能是因为这个 属性 被所有派生的子 class 共享(你不无论如何都要检查派生的 class 类型)。在这种情况下,最合乎逻辑的做法是改为在 super 中实现 属性,这样就解决了问题:
class Base:
x = 3
@classmethod
def get_foo():
return [1, 2, self.x]
您不应该使用 class 属性和方法。您应该定义 class 创建实例并使用实例方法和属性的元素,以便正常的继承按照语言中预期的方式工作。
那么你需要利用实例方法可以被子classes覆盖的事实。这里,为了让parentclass到达childclass的x
,你在[=32]中定义了一个get_x
方法=] class 您希望在 child class 中覆盖。这就是 child class 的 x
被 parent 使用而没有传递给它的方式。
这是一个例子:
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, 2, 3]
在 subclass 上调用的 superclass class 方法中访问 subclass 属性不是问题。这按预期工作:
class Base:
@classmethod
def get_list(cls):
return [1, 2, cls.x]
class Derived(Base):
x = 3
assert Derived.get_list() == [1, 2, 3]
但是,您不能轻易地在子class的class主体中调用那个class方法,因为当时class对象对于 Derived
和 Derived
这个名字确实还不存在。 (你可以在方法中调用它,classmethods 和 staticmathods,因为它们的主体在调用时被评估,而不是在它们被定义时已经评估。但这在这里没有帮助。)如果调用 Base
,明显缺少x
。 (并且作为 Python class 方法,get_list
必须明确地调用某些东西。我们不能像 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 外部创建和设置 class 属性。所以这有效:
class Base:
@classmethod
def get_list(cls):
return [1, 2, cls.x]
class Derived(Base):
x = 3
Derived.foo = Derived.get_list()
assert Derived.foo == [1, 2, 3]
如何在其基本 classes 方法中访问派生的 classes 属性(其他语言中的静态变量)? 即
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, 2, 3]
我不希望将 x 直接传递给基数 class,即 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')
因此在基础管理 class 中,它不断重复使用自定义字段、自定义模型和其他内容的列表。因此,与其每次都传递它们,对于每个 属性,将它们设置为静态属性似乎更 DRY。因此我希望将它们设置为派生 class 上的静态成员字段,并在基础 class.
中访问它们如果你想让 super 访问派生 class 的 属性,可能是因为这个 属性 被所有派生的子 class 共享(你不无论如何都要检查派生的 class 类型)。在这种情况下,最合乎逻辑的做法是改为在 super 中实现 属性,这样就解决了问题:
class Base:
x = 3
@classmethod
def get_foo():
return [1, 2, self.x]
您不应该使用 class 属性和方法。您应该定义 class 创建实例并使用实例方法和属性的元素,以便正常的继承按照语言中预期的方式工作。
那么你需要利用实例方法可以被子classes覆盖的事实。这里,为了让parentclass到达childclass的x
,你在[=32]中定义了一个get_x
方法=] class 您希望在 child class 中覆盖。这就是 child class 的 x
被 parent 使用而没有传递给它的方式。
这是一个例子:
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, 2, 3]
在 subclass 上调用的 superclass class 方法中访问 subclass 属性不是问题。这按预期工作:
class Base:
@classmethod
def get_list(cls):
return [1, 2, cls.x]
class Derived(Base):
x = 3
assert Derived.get_list() == [1, 2, 3]
但是,您不能轻易地在子class的class主体中调用那个class方法,因为当时class对象对于 Derived
和 Derived
这个名字确实还不存在。 (你可以在方法中调用它,classmethods 和 staticmathods,因为它们的主体在调用时被评估,而不是在它们被定义时已经评估。但这在这里没有帮助。)如果调用 Base
,明显缺少x
。 (并且作为 Python class 方法,get_list
必须明确地调用某些东西。我们不能像 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 外部创建和设置 class 属性。所以这有效:
class Base:
@classmethod
def get_list(cls):
return [1, 2, cls.x]
class Derived(Base):
x = 3
Derived.foo = Derived.get_list()
assert Derived.foo == [1, 2, 3]