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

时间:2013-09-14 11:18:51

标签: python oop inheritance nested

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

这是唯一/最好的方式???

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

我当前解决方案的一些简单例子:

# 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()

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

1 个答案:

答案 0 :(得分:3)

仅供参考,在您完成嵌套类时使用的公认术语是内/外,而不是父/子或超/子类。父/子或超/子关系是指继承。这使得装饰者的名字setsubclasses容易混淆,因为没有涉及子类!

你在这里做的不寻常的事情是将类作为命名空间而不实例化它。通常,您将实例化您的ComponentModel,那时,为您的Serialiser内部类提供其外部类的属性副本是微不足道的。 E.g:

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

# ... then

cm  = ComponentModel()
ser = cm.Serialiser()

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

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

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

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()

这不是你的装饰器没有做任何事情,但是用户通过继承自动获取它而不必手动指定它。 Python的禅宗说“明确比隐含更好”,番茄,番茄。

你甚至可以编写元类,以便它对内部类进行内省,并将对该类的引用放入每个内部类中,而不管它们的名称如何。

顺便说一下,你这样做的一个缺陷就是你所有的模型类都必须继承SerialiserBase。如果您班级的用户只想要默认的序列化服务器,他们就不能在他们的班级定义中编写Serialiser = SerialiserBase,他们必须编写class Serialiser(SerialiserBase): pass。这是因为只有一个SerialiserBase,它显然不能包含对多个外部类的引用。当然,您可以编写您的元类来处理此问题(例如,如果已经具有outer属性,则自动生成指定序列化程序的子类)。