简体   繁体   English

Python 以可继承的方式获取当前 class 的副本

[英]Python get a copy of current class in an inheritable way

I have the following setup:我有以下设置:

from copy import deepcopy

class A(object):
    def __init__(self, *args):
        assert all([type(a) is int for a in args])
        self.data = list(args)

    def copy(self):
        cp = A()
        cp.data = self.data.copy()
        cp.__class__ = self.__class__
        return cp

class B(A):
    def do_extra(self):
        print('does extra stuff')


b = B()
l = b.copy()


print(l)
# -> <__main__.B object at 0x10f3b9070>

l.do_extra()
# -> does extra stuff

I want B to inherit the copy() method of A and return a deep copy of the B class, but I'm not sure how to refer to the "constructor" in a way that won't break if I add extra initialisation to B .我希望B继承A的 copy() 方法并返回B class 的深层副本,但我不确定如何以一种不会中断的方式引用“构造函数”,如果我添加额外的初始化到B I'm aware of copy.deepcopy , which is perhaps a better solution, but I'm still not sure if it is the canonical method since it seems to cause issues with complex inheritance structures .我知道copy.deepcopy ,这也许是一个更好的解决方案,但我仍然不确定它是否是规范方法,因为它似乎会导致复杂的 inheritance 结构出现问题

You should be using the deepcopy method which will work without any extra messing around:你应该使用deepcopy方法,它可以在没有任何额外混乱的情况下工作:

import copy

class A(object):
    def __init__(self):
        self.data = 1

class B(A):
    def do_extra(self):
        print('does extra stuff')

b = B()
b_copy = copy.deepcopy(b)

b_copy.do_extra() # works

The other question you linked has issues due to pickling the classes so you probably don't need to do that here based on your example.您链接的另一个问题由于对类进行酸洗而出现问题,因此您可能不需要根据您的示例在此处执行此操作。

While copy.deepcopy is almost certainly the correct way to do this (so please, use QuantumMecha's answer ), you can determine the class of the derived type via self.__class__ (or slightly more Pythonically if you can guarantee Python 3 or new-style classes in Python 2, type(self) ), and it is a callable .虽然copy.deepcopy几乎肯定是执行此操作的正确方法(所以请使用QuantumMecha 的答案),但您可以通过self.__class__确定派生类型的 class (或者如果您可以保证 Python 3 或新样式,则稍微更像 Python Python 2, type(self) ) 中的类,它是一个可调用的 For the specific case shown here, you could implement it as:对于此处显示的特定案例,您可以将其实现为:

def copy(self):
    cp = type(self)()  # Equivalent to A() or B(), depends on whether self is an A or B
    cp.data = self.data.copy()
    return cp

and that would correctly create an instance of B if you called .copy() on an instance of B .如果您在B的实例上调用.copy() ,那将正确创建B的实例。 It won't work if B 's initializer violates the Liskov Substitution Principle (if it does, B would have to override copy itself to do "the right thing", whatever that may be), but as long as the arguments to the initializers are compatible, it would work.如果B的初始化程序违反了 Liskov 替换原则,它将不起作用(如果这样做, B将不得不覆盖copy自身以做“正确的事情”,无论可能是什么),但只要 arguments 到初始化程序兼容,它会工作。

Alternatively, if you want to create a 100% barebones instance and populate it entirely manually (perhaps because original initialization work doesn't make sense or would be incorrect in the context of copying), you could do (for classes that don't override __new__ ):或者,如果您想创建一个 100% 准系统实例并完全手动填充它(可能是因为原始初始化工作没有意义或在复制上下文中不正确),您可以这样做(对于不覆盖的类__new__ ):

def copy(self):
    cls = type(self)
    cp = cls.__new__(cls)  # Makes new instance without invoking __init__
    # *All* initialization must be done here, so don't forget anything
    cp.data = self.data.copy()
    return cp

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM