2024-10-01 09:32:10 发布
网友
我现在在图书馆工作。你知道吗
我想编写一个包装器,用一个新类(假设WindowedMean)修改继承树中的一个类(Mean),并且我想初始化这个类(例如k=10)。你知道吗
WindowedMean
Mean
Mean类可以位于heritage树中的任何位置这只是一个示例。你知道吗
This link shows the example
我知道这是不明智的。 你有优雅的方法吗?你知道吗
我想象一下用这样的包装器:
metric = Wrapper(MyClass, k=10)
也许是这样的?你知道吗
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。你知道吗
k=10
super
然后,只需创建MyClass的新子级,并将Mean的重写子级添加到继承树中。请注意,MyClass的这个子类不需要在其主体中使用单个语句,其行为与MyClass完全相同,只是修改后的Mean现在位于mro的适当位置。你知道吗
MyClass
直接在解释器中(我必须求助于exec才能在一行中键入所有的类层次结构)
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中的方法。你知道吗
Parent2
__mro__
原始答案
它看起来可以与类装饰器一起工作(也可以与您想要的“包装器”语法一起使用)。你知道吗
当然,也有可能出错的情况,但是如果您的树表现得有些好,我们必须递归地获取要包装的类的所有基,并在这些基中进行替换-我们不能简单地获取最后的基,在它的__mro__中展开所有祖先类,然后替换所需的基同学们:遗产线将被打破。你知道吗
也就是说,假设您有类A, B(A), C(B), D(C),并且您想要D的克隆D2,用B2(A)-D.__bases__is C替换B。D.__mro__是(D, C, B, A, object)如果我们尝试创建一个新的D2强制__mro__是 (D, C, B2, A, object),类C将中断,因为它将不再看到B。(这个答案的前一个版本中的代码将把B和B2都留在继承行中,从而导致进一步的代理)。 下面的解决方案不仅重新创建了一个新的B类,而且还创建了一个新的C。你知道吗
A, B(A), C(B), D(C)
D
D2
B2(A)
D.__bases__
C
B
D.__mro__
(D, C, B, A, object)
(D, C, B2, A, object)
请注意,如果B2本身不会从A继承,那么在同一个示例中,A本身将从__mro__中删除,对于新的,被替换的B2不需要它。如果D有任何依赖于A的功能,它将中断。这是不容易修复的,但是要设置检查机制以确保被替换的类包含与被替换的类相同的祖先,否则会引发错误-这很容易做到。但要找出如何挑选和包括“现在失踪”的祖先并不容易,因为根本不可能知道他们是否需要,只是开始而已。你知道吗
B2
A
至于配置部分,如果没有一些关于RollingMean类是如何“配置”的示例,就很难给出一个合适的具体示例—但是让我们为它创建一个子类,用传递的参数更新它的dict—对于任何需要的配置都应该这样做。你知道吗
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)
在那里,我会谨慎地派生适当的元类-如果继承树中没有任何类使用除type以外的元类(通常abc或ORM模型有不同的元类),则可以在调用中使用type而不是types.new_class。你知道吗
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
也许是这样的?你知道吗
更新
尽管下面的解决方案将完全如您所描述的那样工作,但我注意到,对于多重继承,您所要求的可以自然发生。你知道吗
只需继承要修改的类,就可以使用常规继承机制重写继承的类行为。这意味着,如果需要,可以设置
k=10
类属性,并对Mean
的父级进行硬编码超级调用,而不是使用super
。你知道吗然后,只需创建
MyClass
的新子级,并将Mean
的重写子级添加到继承树中。请注意,MyClass的这个子类不需要在其主体中使用单个语句,其行为与MyClass
完全相同,只是修改后的Mean
现在位于mro的适当位置。你知道吗直接在解释器中(我必须求助于
exec
才能在一行中键入所有的类层次结构)注意,有一种情况下,这种方法并不简单:在您的示例中,
super
中的调用Mean
应该调用Parent2
中的方法。在这种情况下,要么求助于原始解决方案,要么使用一些巧妙的__mro__
操作来跳过Mean
中的方法。你知道吗原始答案
它看起来可以与类装饰器一起工作(也可以与您想要的“包装器”语法一起使用)。你知道吗
当然,也有可能出错的情况,但是如果您的树表现得有些好,我们必须递归地获取要包装的类的所有基,并在这些基中进行替换-我们不能简单地获取最后的基,在它的
__mro__
中展开所有祖先类,然后替换所需的基同学们:遗产线将被打破。你知道吗也就是说,假设您有类
A, B(A), C(B), D(C)
,并且您想要D
的克隆D2
,用B2(A)
-D.__bases__
isC
替换B
。D.__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—对于任何需要的配置都应该这样做。你知道吗在那里,我会谨慎地派生适当的元类-如果继承树中没有任何类使用除
type
以外的元类(通常abc或ORM模型有不同的元类),则可以在调用中使用type
而不是types.new_class
。你知道吗最后,在交互式提示中使用此选项的示例:
相关问题 更多 >
编程相关推荐