从 Python 中的嵌套子类访问父变量

SCo*_*vin 6 python oop inheritance nested

我想知道从嵌套子类访问父变量的最佳方法是什么,目前我正在使用装饰器。

这是唯一/最好的方法吗???

我不想直接访问父变量(例如 ComponentModel.origin (见下文)),因为这将需要“配置”文件中的更多代码,所以我想知道是否可以在有问题的子类继承自哪个类?

我当前解决方案的简单示例:

# defined in a big library somewhere:
class LibrarySerialiser(object):
    pass

# defined in my module:
class ModelBase:
    pass

class SerialiserBase(LibrarySerialiser):
    def __init__(self, *args, **kwargs):
        # could i some how get hold of origin here without the decorator?
        print self.origin
        super(SerialiserBase, self).__init__(*args, **kwargs)

def setsubclasses(cls):
    cls.Serialiser.origin = cls.origin
    return cls

# written by "the user" for the particular application as the
# configuration of the module above:

@setsubclasses
class ComponentModel(ModelBase):
    origin = 'supermarket'

    class Serialiser(SerialiserBase):
        pass


ser = ComponentModel.Serialiser()
Run Code Online (Sandbox Code Playgroud)

这显然是一个简单的例子,它错过了所有真正的逻辑,因此许多类看起来无效,但确实是必要的。

kin*_*all 6

仅供参考,您所做的嵌套类时使用的可接受的术语是内部/外部,而不是父/子或超级/子类。父/子或超/子关系是指继承。这使得你的装饰器的名字,setsubclasses令人困惑,因为没有涉及子类!

您在这里所做的不寻常的事情是使用该类作为命名空间而不实例化它。通常情况下,您会实例化您的内部类ComponentModel,此时,为您的Serialiser内部类提供外部类的属性副本是很简单的。例如:

class ModelBase(object):
    def __init__(self):
        self.Serialiser.origin = self.origin

# ... then

cm  = ComponentModel()
ser = cm.Serialiser()
Run Code Online (Sandbox Code Playgroud)

更好的是,让外部类实例化内部类并向其传递对外部类的引用;然后它可以在需要时获取它想要的任何属性:

class ModelBase(object):
    def __init__(self, *args, **kwargs):
        serialiser = self.Serialiser(self, *args, **kwargs)

class SerialiserBase(LibrarySerialiser):
    def __init__(self, outer, *args, **kwargs):
        self.outer = outer
        print self.outer.origin
        super(SerialiserBase, self).__init__(*args, **kwargs)

# ...

cm  = ComponentModel()
ser = cm.serialiser
Run Code Online (Sandbox Code Playgroud)

但是,如果您坚持能够在不实例化外部类的情况下获取此属性,则可以使用元类来设置该属性:

class PropagateOuter(type):
    def __init__(cls, name, bases, dct):
        type.__init__(cls, name, bases, dct)
        if "Serialiser" in dct:
            cls.Serialiser.outer = cls

class ModelBase(object):
    __metaclass__ = PropagateOuter

# Python 3 version of the above
# class ModelBase(metaclass=PropagateOuter):
#     pass

class SerialiserBase(LibrarySerialiser):
    def __init__(self, *args, **kwargs):
        print self.outer.origin
        super(SerialiserBase, self).__init__(*args, **kwargs)

class ComponentModel(ModelBase):
    origin = 'supermarket'

    class Serialiser(SerialiserBase):
        pass

ser = ComponentModel.Serialiser()
Run Code Online (Sandbox Code Playgroud)

这并没有做装饰器没有做的任何事情,但是用户通过继承自动获取它,而不必手动指定它。Python 的禅宗说“显式优于隐式”,所以番茄,番茄。

您甚至可以编写元类,以便它内省外部类并将对该类的引用放入每个内部类中,无论其名称如何。

顺便说一句,您这样做的方式的陷阱之一是所有模型类都必须子类化SerialiserBase。如果您的类的用户只想要默认的序列化器,他们不能只Serialiser = SerialiserBase在类定义中编写,他们必须编写class Serialiser(SerialiserBase): pass. 这是因为只有一个SerialiserBase,而且它显然不能包含对多个外部类的引用。当然,您可以编写元类来处理此问题(例如,如果指定序列化程序已经具有属性,则自动创建其子类outer)。