类,它以另一个类作为参数,复制behavior

2024-04-25 20:24:51 发布

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

我想用Python创建一个类,它接受构造函数中的一个参数,另一个Python类。复制类的实例应该具有原始类的所有属性和方法,而不必事先知道它们应该是什么。下面是一些几乎可以工作的代码:

import copy

class A():
    l = 'a'

class Copy():

    def __init__(self, original_class):
        self = copy.deepcopy(original_class)
        print(self.l)

c = Copy(A)
print(c.l)

构造函数中的print语句打印“a”,但最后一个语句给出错误AttributeError: Copy instance has no attribute 'l'。在


Tags: 实例方法代码importself参数属性init
3条回答

这是一个有趣的问题,它指出了Python传递值语义的一个非常酷的特性,因为它与为什么原始代码不能正确工作以及为什么@martineau的解决方案工作得很好密切相关。在

为什么你写的代码不起作用

Python不支持纯粹的按引用传递或按值传递语义,而是执行以下操作:

# Assume x is an object
def f(x):
    # doing the following modifies `x` globally
    x.attribute = 5 
    # but doing an assignment only modifies x locally!
    x = 10
    print(x)

为了看到这一点

^{pr2}$

会发生什么?赋值创建一个局部x,然后为其赋值。一旦发生这种情况,作为参数传入的原始参数将不可访问。在

但是,只要名称x绑定到传入的对象,您就可以修改属性,它将反映在传入的对象中。但是,在您将名称x让给其他对象的那一刻起,该名称就不再绑定到您传入的原始参数。在


为什么这和这里有关?在

如果您仔细注意__init__的签名,您会注意到它将self作为参数。什么是self?在

通常,self是指对象实例。因此名称self被绑定到对象实例。在

这就是乐趣的开始。通过在代码中赋值给self,这个属性不再有效!

def __init__(self, original_class):
    # The name `self` is no longer bound to the object instance,
    # but is now a local variable!
    self = copy.deepcopy(original_class)
    print(self.l) # this is why this works!

当您离开__init__时,这个新的局部变量self就超出了范围。这就是为什么做c.l会在构造函数之外产生一个错误-您根本就没有实际分配给对象!在

为什么@martineau的解决方案有效

@martineau只是利用这个行为来注意到__dict__属性存在于self对象上,并为其赋值:

class Copy():
    def __init__(self, original_class):
        # modifying attributes modifies the object self refers to!
        self.__dict__ = copy.deepcopy(original_class.__dict__)
        print(self.l)

现在,这是因为当Python看到名称空间操作符.时,当Python需要查找方法签名或属性时,__dict__属性是Python调用的,也是因为{}没有被更改,但仍然引用对象实例。通过赋值给self.__dict__,您将获得原始类的几乎完全相同的副本(“几乎精确”,因为即使是deepcopy也有限制)。在


这个故事的寓意应该很清楚:不要直接给self分配任何东西。相反,如果需要,只将分配给self的属性。Python的元编程允许在这方面有很大的灵活性,在这方面您应该始终咨询the documentation。在

我不确定你为什么要这么做,但你可能有你的理由。在

您可以利用正常继承:

例如:

    class A(object):
        l = 'a'

    class C(object):
        l = 'c'

    def createClass(cls):

        class B(cls):
            pass

        return B

    cls = createClass(C) # or A or whatever
    print cls.l

=> result: 'c'

您需要复制__dict__

import copy

class A():
    l = 'a'

class Copy():
    def __init__(self, original_class):
        self.__dict__ = copy.deepcopy(original_class.__dict__)
        print(self.l)

c = Copy(A)  # -> a
print(c.l)  # -> a

相关问题 更多 >