繁体   English   中英

具有类变量继承的子类

[英]Subclass with class variable inheritance

在父类中,我定义了一个类变量和一个用于修改类变量值的类方法。 我希望每个子类都使用其自己的变量,而不是与其父级共享。

但是结果却出乎我的意料。 在以下示例中,我有两组父类和子类,以及一些代码来演示出了什么问题:

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'}}

第一个示例具有预期的结果,但没有第二个示例,当我在第二组类的子类上调用类方法时,为什么类变量与父类共享?

我的预期结果:

In p cm
1
0
In p cm
1
0
{'mat': {'loader': 'loader 1'}}
{}
{'mat': {'loader': 'loader 2'}}
{}

不同之处在于您对字典进行了变异 第一个简单的整数示例适用于不可变的整数对象。 cls._X += 1_X的值(如有必要,从父类获取),此后old + 1操作生成一个新的整数对象,然后将其分配回cls._X 作业在这里很重要,因为这将在子班上进行。

但是在第二种情况下,您没有将任何东西分配回类:

cls._callbacks[fmt] = {}
cls._callbacks[fmt]["loader"] = loader

你分配到字典的一个关键。 cls._callbacks属性本身未更改,它是所有类之间共享的同一词典。 查找在Image基类上找到的cls._callbacks引用,此后,通过添加键值对来更新字典本身。 子类( HSImageGT )都没有属性本身。

您将需要创建一个副本并分配回更改后的副本:

cls._callbacks = {k: dict(v) for k, v in cls._callbacks.items()}
cls._callbacks[fmt] = {'loader': loader}

这不仅会创建外部字典的副本,还会创建所有值的副本,因为在为fmt添加新字典之前,这些值也都是字典。 然后将该副本分配给cls._callbacks ,从而有效地在子类上创建新属性(如果尚未存在)。

当然那不是那么有效。 每次注册装载程序时都会创建一个副本。 您最好在每个子类上创建一个新的_callback字典对象,但这会很乏味并且很容易被忘记。 您可以改为使用__init_subclass__方法 自动化__init_subclass__

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}

为您创建的每个子类调用__init_subclass__方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM