英文:
How to clone a python class object? (not the instance but the class itself)
问题
假设你有以下的代码:
class A:
pass
NewA = ... # 复制 A
NewA.__init__ = decorator(A.__init__) # 但不要改变 A 的 init 函数,只改变 NewA 的
我正在寻找一种方法来改变克隆类中的一些属性/方法,而其余部分我希望它们与基类对象类似(最好甚至通过 MappingProxyType,这样当 A 变化时,NewA 未改变的逻辑也会反映出这些变化)。
我遇到了这个古老的帖子,其中提出了一些建议,但并不能完全满足我的要求:
- 重用继承
class NewA(A): pass
,这并不完全符合我所期望的结果。 - 使用
type
动态生成一个新的类,并且以某种方式考虑可能发生的各种情况(具有可变属性/描述符/对全局变量的调用……)。 - 使用
copy.deepcopy
是完全错误的(因为类对象的内部数据表示是 MappingProxyType,我们无法复制/深度复制)。
是否有一种方法可以实现这一点,而不是手动处理每个边缘情况,特别是考虑到我们打算复制的基类可能是任何东西(具有元类和自定义 init_subclass 父类,并且包含可变属性和其他内容,还可能包含 __slots__
)?
英文:
Imagine you have the following code:
class A:
pass
NewA = ... # copy A
NewA.__init__ = decorator(A.__init__) # but don't change A's init function, just NewA's
I am looking for a way to change some of the attributes/methods in the cloned class and the rest I want them to be similar to the base class object (preferebly even through MappingProxyType so that when A changes the unchanged logic of NewA reflects the changes as well).
I came across this ancient thread, where there some suggetions which don't fully work:
- Repurposing inheritance
class NewA(A): pass
which doesn't exactly result in what I am looking for - Dynamically generating a new class using
type
and somehow having an eye out for the tons of cases that might happen (having mutable attributes/descriptors/calls to globals ...) - Using
copy.deepcopy
which is totally wrong (since class object's internal data representation is a MappingProxyType which we cannot copy/deepcopy)
Is there a way to still achive this without manually handling every corner case, especially considering the fact that the base class we intend to copy could be anything (with metaclasses and custom init_subclass parents, and a mixture of attributes mutable and what not, and potentially with __slots__
)?
答案1
得分: 1
以下是翻译好的代码部分:
这里是一个谦卑的尝试,帮助你入门。我已经尝试过它,对于具有插槽的类似乎有效。不过,我对它的这一方面不太确定。
import types
import copy
def clone_class(klass):
def _exec_body(ns):
# 不要添加插槽描述符,否则会失败!
ns_no_slots = {
k:v for k,v in vars(klass).items()
if not isinstance(v, types.MemberDescriptorType)
}
ns |= copy.deepcopy(ns_no_slots)
return ns
return types.new_class(
name=klass.__name__,
bases=klass.__bases__,
kwds={"metaclass": type(klass)},
exec_body=_exec_body,
)
现在,这似乎适用于具有`__slots__`的类。唯一可能会使事情变得复杂的是,如果元类具有插槽(必须为空)。但那将真的很奇怪。
这是一个测试脚本:
import types
import copy
def clone_class(klass):
def _exec_body(ns):
ns_no_slots = {
k:v for k,v in vars(klass).items()
if not isinstance(v, types.MemberDescriptorType)
}
ns |= copy.deepcopy(ns_no_slots)
return ns
return types.new_class(
name=klass.__name__,
bases=klass.__bases__,
kwds={"metaclass": type(klass)},
exec_body=_exec_body,
)
class Meta(type):
def meta_magic(cls):
print("magical!")
class Foo(metaclass=Meta):
__slots__ = ('x','y')
@property
def size(self):
return 42
class Bar(Foo):
state = []
__slots__ = ('z',)
def __init__(self, x=1, y=2, z=3):
self.x = x
self.y = y
self.z = z
@property
def closed(self):
return False
BarClone = clone_class(Bar)
bar = BarClone()
BarClone.state.append('foo')
print(BarClone.state, Bar.state)
BarClone.meta_magic()
这将打印:
['foo'] []
magical!
希望这有所帮助。如果您需要进一步的翻译或有其他问题,请告诉我。
英文:
Here is a humble attempt to get you started. I've tested it out with a class with slots and it seems to work. I am not very sure about that aspect of it though.
import types
import copy
def clone_class(klass):
def _exec_body(ns):
# don't add in slots descriptors, it will fail!
ns_no_slots = {
k:v for k,v in vars(klass).items()
if not isinstance(v, types.MemberDescriptorType)
}
ns |= copy.deepcopy(ns_no_slots)
return ns
return types.new_class(
name=klass.__name__,
bases=klass.__bases__,
kwds={"metaclass": type(klass)},
exec_body=_exec_body,
)
Now, this seems to work with classes that have __slots__
. The one thing that might trip things up is if the metaclass has slots (which must be empty). But that would be really weird.
Here is a test script:
import types
import copy
def clone_class(klass):
def _exec_body(ns):
ns_no_slots = {
k:v for k,v in vars(klass).items()
if not isinstance(v, types.MemberDescriptorType)
}
ns |= copy.deepcopy(ns_no_slots)
return ns
return types.new_class(
name=klass.__name__,
bases=klass.__bases__,
kwds={"metaclass": type(klass)},
exec_body=_exec_body,
)
class Meta(type):
def meta_magic(cls):
print("magical!")
class Foo(metaclass=Meta):
__slots__ = ('x','y')
@property
def size(self):
return 42
class Bar(Foo):
state = []
__slots__ = ('z',)
def __init__(self, x=1, y=2, z=3):
self.x = x
self.y = y
self.z = z
@property
def closed(self):
return False
BarClone = clone_class(Bar)
bar = BarClone()
BarClone.state.append('foo')
print(BarClone.state, Bar.state)
BarClone.meta_magic()
This prints:
['foo'] []
magical!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论