在继承树中动态更改给定的类,然后将其替换为另一类并进行初始化

时间:2019-05-08 18:05:04

标签: python class oop metaclass method-resolution-order

我目前正在图书馆工作。

我想编写一个包装,通过一个新类(假设Mean)来修改继承树中的类(示例中的WindowedMean),并且我想初始化< / strong>此类(例如 k = 10 )。

Mean类可以在继承树中的任何位置,这只是一个示例。

This link shows the example

我知道这是不明智的。 你有一个优雅的方法吗?

我想像这样使用这种包装器:


metric = Wrapper(MyClass, k=10)

感谢您关注我的问题。

2 个答案:

答案 0 :(得分:0)

也许是这样吗?

class Parent1(object):
    def __init__(self):
        super(Parent1, self).__init__()

    def speak(self):
        print('Iam parent 1')

class Parent2(object):
    def __init__(self):
        super(Parent2, self).__init__()

    def speak(self):
        print('Iam parent 2')

class Child():
    """docstring for Child."""
    def __init__(self):
        pass

    def child_method(self):
        print('child method')

def make_child(inhert):
    child = Child()

    class ChildInhert(Child, inhert):

        def __init__(self):
            inhert.__init__(self)

    return ChildInhert()

if __name__ == '__main__':
    child = make_child(Parent1)
    child.speak()
    child.child_method()

答案 1 :(得分:0)

更新

尽管下面的解决方案将按照您所描述的完全起作用,但是我注意到,使用多重继承,您的要求可以自​​然发生。

仅继承要修改的类,请使用常规继承机制覆盖继承的类行为。这意味着,设置k=10类属性,并在需要时对Mean的父级进行硬编码超级调用,而不是使用super

然后,只需创建一个MyClass的新子代并将Mean的重写子代添加到继承树。请注意,该MyClass的子类不需要在其主体中使用任何语句,并且其行为与MyClass完全相同,只是修改后的Mean现在位于mro中的适当位置。

直接在解释器中(我不得不诉诸exec才能在一行中键入所有类层次结构

In [623]: exec("class GreatGrandParent1: pass\nclass GreatGrandParent2: pass\nclass GrandParent1(GreatGrandParent1, Gre
     ...: atGrandParent2): pass\nclass Mean: pass\nclass Parent1(GrandParent1, Mean): pass\nclass Parent2: pass\nclass 
     ...: MyClass(Parent1, Parent2): pass")                                                                            

In [624]: MyClass.__mro__                                                                                              
Out[624]: 
(__main__.MyClass,
 __main__.Parent1,
 __main__.GrandParent1,
 __main__.GreatGrandParent1,
 __main__.GreatGrandParent2,
 __main__.Mean,
 __main__.Parent2,
 object)

In [625]: class MeanMod(Mean): 
     ...:     k = 10 
     ...:                                                                                                              

In [626]: class MyClassMod(MyClass, MeanMod): pass                                                                     

In [627]: MyClassMod.__mro__                                                                                           
Out[627]: 
(__main__.MyClassMod,
 __main__.MyClass,
 __main__.Parent1,
 __main__.GrandParent1,
 __main__.GreatGrandParent1,
 __main__.GreatGrandParent2,
 __main__.MeanMod,
 __main__.Mean,
 __main__.Parent2,
 object)

请注意,在某些情况下这种方法无法直接实现:如果在您的示例中,在super中进行的Mean调用应该在Parent2中进行了调用。在这种情况下,要么求助于原始解决方案,要么使用一些聪明的__mro__操作来跳过Mean中的方法。

原始答案

看起来这可以与类装饰器一起使用(也可以与所需的“包装器”语法一起使用)。

肯定有一些极端情况,并且可能会出错-但是,如果您的树表现良好,则必须递归地采用 all 您要包装的类的基础,并在这些基础中进行替换-我们不能简单地采用最后的基础,在其__mro__中展开所有祖先类,然后在其中替换所需的类:传统行将在其后折断。

也就是说,假设您有A, B(A), C(B), D(C)类,并且您想要D2的克隆D,用B-{{1替换B2(A) }}是D.__bases__CD.__mro__,如果我们尝试创建一个新的(D, C, B, A, object),则将D2强制设置为  __mro__的类(D, C, B2, A, object)将中断,因为它将不再看到C。 (并且此答案的先前版本中的代码会将B和B2都留在继承行中,从而导致进一步的经纪业务)。 下面的解决方案不仅重新创建了一个新的B类,而且还重新创建了一个新的B

请注意,如果C本身不会继承自B2,则在同一示例中,A本身也会从A中删除,以替换新的B2。不需要它。如果__mro__具有依赖于D的功能,则它将中断。这不容易解决,但是要建立检查机制以确保要替换的类包含与替换后的类相同的祖先,否则会引发错误-这很容易做到。但是弄清楚如何挑选和包括“现在失踪”的祖先并不容易,因为不可能一开始就知道是否根本需要它们。

对于配置部分,没有一些如何配置A类的示例,很难给出一个适当的具体示例-但让我们对其进行子类化,使用传递的参数更新其字典-应该可以进行所需的任何配置。

RollingMean

我在这里要小心地派生适当的元类-如果您的继承树中没有任何类使用from types import new_class def norm_name(config): return "_" + "__".join(f"{key}_{value}" for key, value in config.items()) def ancestor_replace(oldclass: type, newclass: type, config: dict): substitutions = {} configured_newclass = type(newclass.__name__ + norm_name(config), (newclass,), config) def replaced_factory(cls): if cls in substitutions: return substitutions[cls] bases = cls.__bases__ new_bases = [] for base in bases: if base is oldclass: new_bases.append(configured_newclass) else: new_bases.append(replaced_factory(base)) if new_bases != bases: new_cls = new_class(cls.__name__, tuple(new_bases), exec_body=lambda ns: ns.update(cls.__dict__.copy())) substitutions[cls] = new_cls return new_cls return cls return replaced_factory # canbe used as: #MyNewClass = ancestor_replace(Mean, RollingMean, {"ks": 10})(MyClass) 以外的元类(通常ABC或ORM模型具有不同的元类),则可以使用{{1 }},而不是通话中的type

最后,在交互式提示中使用此示例:

type