如何调用 class 变量而不是同名的子变量?

How can I call a class variable instead of the child variable with the same name?

一如既往,我首先要说我不是任何意义上的专家或专业人士,所以请评价我的温柔。不幸的是,我找不到这个问题的解决方案,但我敢肯定这不是第一次出现在这里。因此,如果您向我介绍相关主题,我将不胜感激。

我有一个 12 层结构的 classes 和 subclasses。类似于:

class A:
    pass
class B(A):
    pass
#class M,N,O(A)....
class C(B):
    pass

等等。

每个 class 和 subclass 可能有或没有(通常他们有)一个 属性(内存['prop'])。任何对象的每个 init 都引用它的超 class.

class A:
    memory={'prop':{'a':0,'b':1}}
    def __init__(self,**kwargs):
       for k in kwargs|self.memory['prop']:
            if k not in self.__dict__:
                setattr(self,k,(kwargs|self.memory['prop'])[k])
class B(A):
    def __init__(self,**kwargs):
        super().__init__(**kwargs)
        for k in self.memory['prop']:
            if k not in self.__dict__:
                setattr(self,k,(self.memory['prop'])[k])
class C(B):
    memory={'prop':{'c':2,'d':3}}
    def __init__(self,**kwargs):
        super().__init__(**kwargs)
        for k in self.memory['prop']:
            if k not in self.__dict__:
                setattr(self,k,(self.memory['prop'])[k])

这里的问题是,如果我创建一个 C 对象,每次算法在 init[= 中调用变量时,它都会调用 C.memory['prop'] 46=] 函数。我需要的是调用 'current' class 内存。

在示例中,如果我 运行 object=C(**{'e'=4}),我将得到一个具有属性 'c''d''e' 的对象,但不是 'a''b'.

我知道我可以使用:

class A:
    memory={'prop':{'a':0,'b':1}}
    def __init__(self,**kwargs):
       for k in kwargs|A.memory['prop']:
            if k not in self.__dict__:
                setattr(self,k,(kwargs|A.memory['prop'])[k])
class B(A):
    def __init__(self,**kwargs):
        super().__init__(**kwargs)
        for k in B.memory['prop']:
            if k not in self.__dict__:
                setattr(self,k,(B.memory['prop'])[k])
class C(B):
    memory={'prop':{'c':2,'d':3}}
    def __init__(self,**kwargs):
        super().__init__(**kwargs)
        for k in C.memory['prop']:
            if k not in self.__dict__:
                setattr(self,k,(C.memory['prop'])[k])

但这意味着我每次定义 class(大约有一千个)时都必须做额外的工作

我也可以这样做:

class A:
    memory={'prop':{'a':0,'b':1}}
    def __init__(self,clas,**kwargs):
       for k in kwargs|clas.memory['prop']:
            if k not in self.__dict__:
                setattr(self,k,(kwargs|clas.memory['prop'])[k])
class B(A):
    def __init__(self,clas,**kwargs):
        super().__init__(clas,**kwargs)
        for k in clas.memory['prop']:
            if k not in self.__dict__:
                setattr(self,k,(clas.memory['prop'])[k])
class C(B):
    memory={'prop':{'c':2,'d':3}}
    def __init__(self,clas,**kwargs):
        super().__init__(clas,**kwargs)
        for k in clas.memory['prop']:
            if k not in self.__dict__:
                setattr(self,k,(clas.memory['prop'])[k])

但这意味着我每次创建对象 object=C(C) 时都必须调用相同的 class,这看起来是错误的。

最好的方法是什么?

您希望 prop 成为一个 class 属性,但每个子 class.

需要一个新的实例

一件简单的事情是在 __init_subclass__ 方法中创建一个 prop:这是每次创建子 class 时语言调用的保留方法:

from copy import deepcopy

class A:
    memory={'prop':{'a':0,'b':1}}
    def __init__(self,**kwargs):
       cls = type(self) # it is better to be specific that we want to update a class property
       for k in kwargs|self.memory['prop']:
            if k not in self.__dict__:
                setattr(self,k,(kwargs|cls.memory['prop'])[k])

    def __init_subclass__(cls, *args, **kw):
        super().__init_subclass__(cls, *args, **kw)
        # this will create a copy of A.memory for each subclass.
        # (__class__ is a special value that always points to THIS (A)
        # class, while "cls", received as a parameter, refers to the
        # subclass that is being created: 
        cls.memory = deepcopy(__class__.memory)
        # if you prefer each subclass to start with an empty memory:
        # cls.memory = {}

class B(A):
    def __init__(self,**kwargs):
        super().__init__(**kwargs)
        # no need for any other code in the subclasses __init__ - 
        # the code on the superclass will store it on the apropriate 
        # dictionary for each class

您似乎很高兴滥用新的字典联合运算符。你不仅 运行 每次你想访问一个键时它都在循环中重复,而且它掩盖了你的实际设计,这可能是有缺陷的:

  1. 应用kwargs
  2. 用 class 级默认值覆盖它们

请记住联合运算符对于字典是不可交换的:在发生冲突时始终使用最后一个右手运算符的值。这与通常做这些事情的方式相反(kwargs 在那个时候有什么意义?),但让我们暂时忽略它。

您似乎希望继承链中的 class 能够更新 __init__ 中的 __dict__。这从根本上与继承相反,继承隐藏了父属性。您可以将字典更新吸收到方法调用中,可以使用 super 确保处理整个链:

class A:
    def __init__(self, **kwargs):
        self.__dict__.update(self.get_defaults())
        self.__dict__.update(kwargs)

     @classmethod
     def set_defaults(cls):
         return {'A': 1, 'B': 2}

class B(A):
    pass

class C(B):
    @classmethod
    def get_defaults(cls):
        return super().get_defaults() | {'b': 3, 'c': 4}

这样,您只需要一个构造函数。它还具有允许您即时生成默认值的潜在优势。