是否可以与子类的实例共享子类insatnces中定义的方法引用?

时间:2017-05-27 18:39:45

标签: python dictionary python-3.5 python-decorators

我有一个名为A的课程:

>>> class A:
    def __init__(self):
        self.register = {}


>>> 

班级A将由班级B进行细分。但是,类B包含需要在类name: function字典的实例中注册为A对的方法。这样,类A可以使用这些方法工作。

这是我的意思的一个例子:

>>> class B(A):
    def foo(self):
        pass
    def bar(self):
        pass


>>> b = B()
>>> b.register # foo and bar were registered
{'key': <foo function>, 'key2': <bar function>}
>>> 

有解决这个问题的惯用方法吗?或者这样的事情是不可能的,最好改变我的代码结构。

请注意,这不是Auto-register class methods using decorator的副本,因为我的寄存器不是全局的,它是类的实例变量。这意味着使用所选答案中显示的元类将无效。

2 个答案:

答案 0 :(得分:2)

如果我理解正确的话,我认为您在帖子中提到的this question链接确实可用于解决您的问题。

您尝试注册的信息是全球信息。虽然您希望每个实例都有一个包含此全局信息的寄存器,但您真正需要做的就是将__init__复制全局寄存器到实例寄存器中。

答案 1 :(得分:1)

如果您要声明所需的所有类,并且在此之后担心 instance 寄存器对所有子类中的所有声明的方法都有引用,您只需要执行&#34; register&#34;声明子类本身时的信息。使用新的__init_subclass__ mechanism在Python 3.6(但不是3.5)中很容易做到这一点。在Python 3.5中,在此之前,使用元类更容易执行。

class FallbackDict(dict):
    def __init__(self, fallback):
         self.fallback = fallback
    def __missing__(self, key):
          value = self.fallback[key]
          self[key] = value
          return value

class A:
    register = {}
    def __init__(self):
          # instance register shadows global register
          # for access via "self."
          self.register = FallbackDict(__class__.register)

    def __init_subclass__(cls):
          for attrname, value in cls.__dict__.items():
               if callable(value):
                    __class__.register[attrname] = value

这里的代码很简单 - 自定义dict类将&#34;复制读取&#34; A.regiser字典的值到实例字典中。如果您需要包含此行为的更一致的字典(例如,正确迭代其自身和后备字典的键,值和项目的字典),您可以更好地实现&#34; FallbackDict&#34; class作为collections.abc.MutableMapping的实例(而只是使用聚合字典来实际保存数据)

如果您计划在创建&#34; A&#34;的任何实例之前创建所有注册新方法的类,则根本不需要自定义dict。 - 或者如果新创建的类不必更新现有实例 - 但在这种情况下,您应该将A.register复制到实例&#34; self.register&#34;在__init__内。

如果您无法更改为Python 3.6,您还需要一个自定义元类来触发上面的__init_subclass__方法。保持原样,以便您的代码可以移动到Python 3.6并消除元类,并添加类似于以下内容的元类:

class MetaA(type):
    def __init__(cls, name, bases, namespace):
         super().__init__(name, bases, namespace)
         cls.__init_subclass__()

class A:
    ...
    @clasmethod
    def __init_subclass__(cls):
        # as above
        ...