【问题标题】:python: simulating the multiple inheritance of class variablespython:模拟类变量的多重继承
【发布时间】:2020-11-11 07:14:58
【问题描述】:

我有一个类层次结构,其中一些方法使用在类级别定义的属性列表。

假设对于A 类,我有A.X = [propA1, propA2],对于C 子类,我需要C.X = [propA1, propA2, propC]。子类从父类继承属性,因此使用super() 调用编写类方法是有意义的,每个方法都使用自己类的属性。

但是,它有点麻烦。我可以在基类的一个方法中处理所有属性。因此,为每个子类定义一个包含一组新属性的类变量并手动向下 cls.__mro__ 检索所有属性真的感觉更自然。

我想出的(下)似乎相对透明,但它是惯用的吗?是否有更典型的编码模式?有没有办法避免装饰所有子类?

class Base(object):
    pass

class InheritClassVariable:
    def __init__(self, var, base):
        self.var = var
        self.base = base
    def __call__(self, cls):
        name = self.var
        uname = '_' + name
        bases = [B for B in cls.__mro__ if issubclass(B, self.base)]
        setattr(cls, uname, getattr(cls, name, []))
        value = [item for B in bases for item in getattr(B, uname, [])]
        setattr(cls, name, value)
        return cls

@InheritClassVariable('X', Base)
class A(Base):
    X = ['propA1', 'propA2']

@InheritClassVariable('X', Base)
class B(Base):
    X = ['propB']

@InheritClassVariable('X', Base)
class C(A):
    X = ['propC']

@InheritClassVariable('X', Base)
class D(C,B,A):
    X = ['propD']

if __name__ == "__main__":
    print(f"D.X = {D.X}")

【问题讨论】:

  • 我通过使用元类回答了大致相似的问题。 stackoverflow.com/a/62164653/1394353 habits 似乎等同于X。元类通常是多余的,但对于处理祖先 class 而不是 instance 层次结构很有用。还是一定要谨慎使用。
  • 谢谢,我查了元类是什么,并且可以在简单的情况下做到这一点。然后我看到 init_subclass 可用于避免子类,我确实设法以更简单的方式做到这一点。
  • 是的,忘了说。切普纳的回答,如果我记得的话。这是一个新功能,直到大约 6 个月前才听说过。不管你怎么做,都要非常小心类级别的可变属性,比如列表。并不是说你不应该使用它们,但你不能让实例再次变异它们。如果需要,请先执行self.X = self.X.copy() 之类的操作。或self.X = self.X + [var] 不是self.X.append(var)
  • 是的,它们是常量。如果它们是可变的,get() 方法会更有意义。顺便说一句,如果两个具有自己元类的层次结构交叉,元类解决方案就会变得尴尬。

标签: python multiple-inheritance class-variables


【解决方案1】:

一位评论员提到了元类,这是我不知道的。我查了一下,发现有一个__init_subclass__ 方法是为了避免使用元类。

众所周知,我可以将代码简化为(已编辑):

def InheritConstantArray(varname, value=[]):
    """Return a class making the 'varname' array to be inherited in subclasses"""
    basename = f"InheritConstantArray{varname}"

    def init_subclass(cls):

        # it seems __class__ won't work here.  I still don't understand
        # why.  All I know is eval() is dirty so I do a lookup.
        allbases = cls.mro()
        base = [b for b in allbases if b.__name__ == basename][0]

        # collaborate with other classes using __init_subclass__().
        # if we want sevaral variables to be inherited.
        super(base, cls).__init_subclass__()

        # base._X[cls] will store original cls.X
        uvarname = f'_{varname}' if varname[0] != '_' else f'{varname}_'
        if not hasattr(base, uvarname):
            setattr(base, uvarname,  {base: value})
        stored_values = getattr(base, uvarname)
        stored_values[cls] = cls.__dict__.get(varname, [])

        # Then we set cls.X value from base classes
        bases = [b for b in allbases if issubclass(b, base)]
        values = [i for b in bases for i in stored_values.get(b, [])]
        print(cls, base)
        setattr(cls, varname, values)

    dct = {varname: value, '__init_subclass__': init_subclass}
    base = type(basename, (object,), dct)

    return base

class A(InheritConstantArray('X')):
    X = ['A']

class B(A):
    X = ['B']

class C(A):
    X = ['C']

class D(B,C,InheritConstantArray('Y')):
    X = ['D']
    Y = ['d']

class E(D):
    X = ['E']
    Y = ['e']

class F(D):
    X = ['F']
    Y = ['f']

class G(E,F):
    X = ['G']
    Y = ['g']

if __name__ == "__main__":
    print(f"A.X = {A.X}")
    print(f"B.X = {B.X}")
    print(f"C.X = {C.X}")
    print(f"D.X = {D.X} {D.Y}")
    print(f"E.X = {E.X} {E.Y}")
    print(f"F.X = {F.X} {F.Y}")
    print(f"G.X = {G.X} {G.Y}")


不过,我仍然不确定这是否是标准方法。 (是的,在我的实际问题中具有类变量和多重继承是有充分理由的。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-11-19
    • 2011-10-15
    • 1970-01-01
    • 2018-06-13
    • 2013-02-11
    • 2020-06-08
    • 1970-01-01
    • 2018-02-24
    相关资源
    最近更新 更多