【问题标题】:Is it possible to share a method reference defined in subclass insatnces with instances of the parent class?是否可以与父类的实例共享子类实例中定义的方法引用?
【发布时间】:2017-10-28 11:03:41
【问题描述】:

我有一个班级叫A

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


>>> 

A 类将被B 类子分类。但是,B 类包含需要在A 类的字典实例中注册为name: function 对的方法。这样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 的重复,因为我的寄存器不是全局的,它是一个类的实例变量。这意味着使用所选答案中所示的元类将不起作用。

【问题讨论】:

  • 你能在底部添加一个 TL;DR 吗?
  • 这是干什么用的;你想用它实现什么样的行为?您不能在类定义时访问实例属性; 没有没有实例。
  • @jonrsharpe 我猜你是对的。我试图实现一个基于正则表达式的词法分析器。我试图允许用户使用装饰器将某个正则表达式模式映射到一个函数。这样,只要匹配了特定的正则表达式,就会调用该函数。我认为使用装饰器会是一种简洁的方法。
  • 你的 TL;DR 只是标题,而 still 实际上并没有提及 你想要做什么...这个当前是 XY 问题。
  • @jonrsharpe 我很抱歉,但我不确定如何简洁地描述我的问题。但是,你的权利。回顾我的代码后,我发现这有点像 XY 问题。我将编辑我的问题以澄清。

标签: python dictionary python-3.5 python-decorators


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    如果你要声明你需要的所有类,然后担心instance注册器引用了所有子类中所有声明的方法,你只需要在声明子类时执行一个“注册”信息他们自己。在 Python 3.6(但不是 3.5)中,使用新的 __init_subclass__ mechanism 很容易做到这一点。在 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 类会将 A.regiser 字典的“读取时复制”值复制到实例字典中。如果您需要包含此行为的更一致的字典(例如,将正确迭代其自身和备用字典的键、值和项的字典),您最好将“FallbackDict”类实现为集合的实例。 abc.MutableMapping 代替(并且只使用聚合字典来实际保存数据)

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

    如果您无法更改为 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
            ...   
    

    【讨论】:

      猜你喜欢
      • 2021-08-28
      • 1970-01-01
      • 2013-06-10
      • 1970-01-01
      • 2019-01-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多