【问题标题】:Accessing a parent variable from a nested child class in Python从 Python 中的嵌套子类访问父变量
【发布时间】:2013-09-14 11:18:51
【问题描述】:

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

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

我不想直接访问父变量(例如 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()

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

【问题讨论】:

    标签: python oop inheritance nested


    【解决方案1】:

    仅供参考,您在嵌套类时使用的公认术语是内部/外部,而不是父/子或超/子类。父/子或超级/子关系是指继承。这会让您的装饰器名称setsubclasses 令人困惑,因为没有涉及到子类!

    您在这里所做的不同寻常的事情是将类用作命名空间而不实例化它。通常你会实例化你的ComponentModel,在那个时候,给你的Serialiser内部类一个来自其外部类的属性的副本是微不足道的。例如:

    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 属性,则通过自动创建指定序列化程序的子类)。

    【讨论】:

    • 非常感谢您的回复,非常有帮助。我认为您的第一个解决方案满足所有要求,很清楚(我怎么没想到我无法想象),并且在配置文件中很简单。
    猜你喜欢
    • 2011-04-08
    • 2015-10-02
    • 2015-08-13
    • 2022-06-23
    • 1970-01-01
    • 1970-01-01
    • 2012-06-04
    • 2015-04-17
    • 2019-08-10
    相关资源
    最近更新 更多