Subclass with class 变量继承
Subclass with class variable inheritance
在 parent class 中,我定义了一个 class 变量和一个 class 方法来修改 class 变量值。我希望每个 child class 使用自己的变量,而不是与其 parent 共享。
但是结果不是我所期望的;在下面的示例中,我有两组 parent class 加上 child classes,以及一些代码来演示出了什么问题:
class P:
_X = 0
@classmethod
def cm(cls):
print("In p cm")
cls._X += 1
class C1(P):
pass
class C2(P):
pass
class Image:
_callbacks = {}
@classmethod
def registerDataFormat(cls, fmt, loader):
if fmt in cls._callbacks.keys():
print("The %s format has already been registered." % (fmt))
return False
cls._callbacks[fmt] = {}
cls._callbacks[fmt]["loader"] = loader
class HSImage(Image):
pass
class GT(Image):
pass
if __name__ == '__main__':
C1.cm()
print(C1._X)
print(P._X)
C2.cm()
print(C2._X)
print(P._X)
HSImage.registerDataFormat("mat", "loader 1")
print(HSImage._callbacks)
print(Image._callbacks)
GT.registerDataFormat("mat", "loader 2")
print(GT._callbacks)
print(Image._callbacks)
结果如下:
In p cm
1
0
In p cm
1
0
{'mat': {'loader': 'loader 1'}}
{'mat': {'loader': 'loader 1'}}
The mat format has already been registered.
{'mat': {'loader': 'loader 1'}}
{'mat': {'loader': 'loader 1'}}
第一个例子有预期的结果,但第二个没有,为什么我调用class方法时class变量与parentclass共享child class 第二组 classes?
我的预期结果:
In p cm
1
0
In p cm
1
0
{'mat': {'loader': 'loader 1'}}
{}
{'mat': {'loader': 'loader 2'}}
{}
区别在于您改变了字典。第一个简单的整数示例使用不可变整数 objects。 cls._X += 1
取 _X
的值(如果需要,来自 parent class),之后 old + 1
操作产生一个新的整数 object然后分配回 cls._X
。这里的分配很重要,因为它将在 child class.
上进行
但是在第二种情况下,您没有将任何东西分配回 class:
cls._callbacks[fmt] = {}
cls._callbacks[fmt]["loader"] = loader
您在字典中指定了一个键。 cls._callbacks
属性本身没有改变,它是所有 classes 共享的同一个字典。查找 cls._callbacks
引用,在 Image
基础 class 上找到,之后通过添加 key-value 对更新字典本身。 None 的子class(HSImage
或 GT
)本身具有属性。
您需要创建一个副本并将更改后的副本分配回去:
cls._callbacks = {k: dict(v) for k, v in cls._callbacks.items()}
cls._callbacks[fmt] = {'loader': loader}
这不仅会创建外部字典的副本,还会创建所有值的副本,因为在为 fmt
添加新字典之前,这些值也是所有字典。然后将副本分配给 cls._callbacks
,有效地在子class 上创建一个新属性(如果它还不存在的话)。
当然效率不高;每次注册加载器时都会创建副本。您最好在每个子 class 上创建一个新的 _callback
字典 object,但这会变得乏味并且很容易被遗忘。您可以使用 自动执行 __init_subclass__
method:
class Image:
def __init_subclass__(cls):
cls._callbacks = {}
@classmethod
def registerDataFormat(cls, fmt, loader):
if fmt in cls._callbacks:
print("The {} format has already been registered.".format(fmt))
return
cls._callbacks[fmt] = {'loader': loader}
您创建的每个子class都会调用__init_subclass__
方法。
在 parent class 中,我定义了一个 class 变量和一个 class 方法来修改 class 变量值。我希望每个 child class 使用自己的变量,而不是与其 parent 共享。
但是结果不是我所期望的;在下面的示例中,我有两组 parent class 加上 child classes,以及一些代码来演示出了什么问题:
class P:
_X = 0
@classmethod
def cm(cls):
print("In p cm")
cls._X += 1
class C1(P):
pass
class C2(P):
pass
class Image:
_callbacks = {}
@classmethod
def registerDataFormat(cls, fmt, loader):
if fmt in cls._callbacks.keys():
print("The %s format has already been registered." % (fmt))
return False
cls._callbacks[fmt] = {}
cls._callbacks[fmt]["loader"] = loader
class HSImage(Image):
pass
class GT(Image):
pass
if __name__ == '__main__':
C1.cm()
print(C1._X)
print(P._X)
C2.cm()
print(C2._X)
print(P._X)
HSImage.registerDataFormat("mat", "loader 1")
print(HSImage._callbacks)
print(Image._callbacks)
GT.registerDataFormat("mat", "loader 2")
print(GT._callbacks)
print(Image._callbacks)
结果如下:
In p cm
1
0
In p cm
1
0
{'mat': {'loader': 'loader 1'}}
{'mat': {'loader': 'loader 1'}}
The mat format has already been registered.
{'mat': {'loader': 'loader 1'}}
{'mat': {'loader': 'loader 1'}}
第一个例子有预期的结果,但第二个没有,为什么我调用class方法时class变量与parentclass共享child class 第二组 classes?
我的预期结果:
In p cm
1
0
In p cm
1
0
{'mat': {'loader': 'loader 1'}}
{}
{'mat': {'loader': 'loader 2'}}
{}
区别在于您改变了字典。第一个简单的整数示例使用不可变整数 objects。 cls._X += 1
取 _X
的值(如果需要,来自 parent class),之后 old + 1
操作产生一个新的整数 object然后分配回 cls._X
。这里的分配很重要,因为它将在 child class.
但是在第二种情况下,您没有将任何东西分配回 class:
cls._callbacks[fmt] = {}
cls._callbacks[fmt]["loader"] = loader
您在字典中指定了一个键。 cls._callbacks
属性本身没有改变,它是所有 classes 共享的同一个字典。查找 cls._callbacks
引用,在 Image
基础 class 上找到,之后通过添加 key-value 对更新字典本身。 None 的子class(HSImage
或 GT
)本身具有属性。
您需要创建一个副本并将更改后的副本分配回去:
cls._callbacks = {k: dict(v) for k, v in cls._callbacks.items()}
cls._callbacks[fmt] = {'loader': loader}
这不仅会创建外部字典的副本,还会创建所有值的副本,因为在为 fmt
添加新字典之前,这些值也是所有字典。然后将副本分配给 cls._callbacks
,有效地在子class 上创建一个新属性(如果它还不存在的话)。
当然效率不高;每次注册加载器时都会创建副本。您最好在每个子 class 上创建一个新的 _callback
字典 object,但这会变得乏味并且很容易被遗忘。您可以使用 自动执行 __init_subclass__
method:
class Image:
def __init_subclass__(cls):
cls._callbacks = {}
@classmethod
def registerDataFormat(cls, fmt, loader):
if fmt in cls._callbacks:
print("The {} format has already been registered.".format(fmt))
return
cls._callbacks[fmt] = {'loader': loader}
您创建的每个子class都会调用__init_subclass__
方法。