动态更改遗产树中的给定类,并用另一个类替换它,然后初始化i

2024-10-01 09:32:10 发布

您现在位置:Python中文网/ 问答频道 /正文

我现在在图书馆工作。你知道吗

我想编写一个包装器,用一个新类(假设WindowedMean)修改继承树中的一个类(Mean),并且我想初始化这个类(例如k=10)。你知道吗

Mean类可以位于heritage树中的任何位置这只是一个示例。你知道吗

This link shows the example

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

我想象一下用这样的包装器:

metric = Wrapper(MyClass, k=10)

Tags: the方法示例图书馆examplelinkmeanthis
2条回答

也许是这样的?你知道吗

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

更新

尽管下面的解决方案将完全如您所描述的那样工作,但我注意到,对于多重继承,您所要求的可以自然发生。你知道吗

只需继承要修改的类,就可以使用常规继承机制重写继承的类行为。这意味着,如果需要,可以设置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中的方法。你知道吗

原始答案

它看起来可以与类装饰器一起工作(也可以与您想要的“包装器”语法一起使用)。你知道吗

当然,也有可能出错的情况,但是如果您的树表现得有些好,我们必须递归地获取要包装的类的所有基,并在这些基中进行替换-我们不能简单地获取最后的基,在它的__mro__中展开所有祖先类,然后替换所需的基同学们:遗产线将被打破。你知道吗

也就是说,假设您有类A, B(A), C(B), D(C),并且您想要D的克隆D2,用B2(A)-D.__bases__is C替换BD.__mro__(D, C, B, A, object)如果我们尝试创建一个新的D2强制__mro__(D, C, B2, A, object),类C将中断,因为它将不再看到B。(这个答案的前一个版本中的代码将把B和B2都留在继承行中,从而导致进一步的代理)。 下面的解决方案不仅重新创建了一个新的B类,而且还创建了一个新的C。你知道吗

请注意,如果B2本身不会从A继承,那么在同一个示例中,A本身将从__mro__中删除,对于新的,被替换的B2不需要它。如果D有任何依赖于A的功能,它将中断。这是不容易修复的,但是要设置检查机制以确保被替换的类包含与被替换的类相同的祖先,否则会引发错误-这很容易做到。但要找出如何挑选和包括“现在失踪”的祖先并不容易,因为根本不可能知道他们是否需要,只是开始而已。你知道吗

至于配置部分,如果没有一些关于RollingMean类是如何“配置”的示例,就很难给出一个合适的具体示例—但是让我们为它创建一个子类,用传递的参数更新它的dict—对于任何需要的配置都应该这样做。你知道吗

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)

在那里,我会谨慎地派生适当的元类-如果继承树中没有任何类使用除type以外的元类(通常abc或ORM模型有不同的元类),则可以在调用中使用type而不是types.new_class。你知道吗

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

In [152]: class A: 
     ...:     pass 
     ...:  
     ...: class B(A): 
     ...:     pass 
     ...:  
     ...: class B2(A): 
     ...:     pass 
     ...:  
     ...: class C(B): 
     ...:     pass 
     ...:  
     ...: class D(B): 
     ...:     pass 
     ...:  
     ...: class E(D): 
     ...:     pass 
     ...:                                                                                                                         

In [153]: E2 = ancestor_replace(B, B2, {"k": 20})(E)                                                                              

In [154]: E.__mro__                                                                                                               
Out[154]: (__main__.E, __main__.D, __main__.B, __main__.A, object)

In [155]: E2.__mro__                                                                                                              
Out[155]: 
(__main__.E_modified,
 __main__.D_modified,
 __main__.B2_k_20,
 __main__.B2,
 __main__.A,
 object)

In [156]: E2.k                                                                                                                    
Out[156]: 20

相关问题 更多 >