以另一个类作为参数的类,复制行为 [英] Class that takes another class as argument, copies behavior

查看:120
本文介绍了以另一个类作为参数的类,复制行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在Python中创建一个类,该类在构造函数中采用单个参数,即另一个Python类. Copy类的实例应具有原始类的所有属性和方法,而无需事先知道它们应该是什么.这是一些几乎可行的代码:

I'd like to create a class in Python that takes a single argument in the constructor, another Python class. The instance of the Copy class should have all the attributes and methods of the original class, without knowing what they should be beforehand. Here's some code that almost works:

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'.

The print statement in the constructor prints 'a', but the final one gives the error AttributeError: Copy instance has no attribute 'l'.

推荐答案

这是一个有趣的问题,它指出了Python传递值语义的一个很酷的功能,因为它与原始代码为什么没有紧密地联系在一起"不能正常工作,以及@martineau的解决方案为何行之有效.

This is an interesting question to point out a pretty cool feature of Python's pass-by-value semantics, as it is intimately tied to why your original code doesn't work correctly and why @martineau's solution works well.

Python不支持纯按引用传递或按值传递语义-相反,它执行以下操作:

Python doesn't support pure pass-by-reference or pass-by-value semantics - instead, it does the following:

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

要查看实际效果,

# example
class Example(object):
    def __init__(self):
        pass

 x = Example()

 print(x)
 >>> <__main__.Example instance at 0x020DC4E0>

 f(e) # will print the value of x inside `f` after assignment 
 >>> 10

 print(x) # This is unchanged
 >>> <__main__.Example instance at 0x020DC4E0>
 e.attribute # But somehow this exists!
 >>> 5

会发生什么?分配会创建一个 local x,然后为其分配一个值.一旦发生这种情况,就无法访​​问作为参数传递的原始参数.

What happens? Assignment creates a local x which is then assigned a value. Once this happens, the original parameter that was passed in as an argument is inaccessible.

但是,只要将 name x绑定到所传递的对象,就可以修改属性,并且属性将反映在所传递的对象中.将名称x替换为其他名称,但是该名称不再绑定到您传入的原始参数.

However, so long as the name x is bound to the object that's passed in, you can modify attributes and it will be reflected in the object you passed in. The minute you 'give away' the name x to something else, however, that name is no longer bound to the original parameter you passed in.

为什么这与这里相关?

Why is this relevant here?

如果您特别注意__init__的签名,您会发现它以self作为参数.什么是self?

If you pay careful attention to the signature for __init__, you'll notice it takes self as a parameter. What is self?

通常,self是指对象实例.因此,名称self绑定到该对象实例.

Ordinarily, self refers to the object instance. So the name self is bound to the object instance.

这是乐趣的开始. 通过在代码中分配给self,此属性不再适用!

This is where the fun starts. By assigning to self in your code, this property no longer holds true!

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会在构造函数之外产生错误的原因-您从来没有真正将其分配给对象!

The minute you leave __init__, this new local variable self goes out of scope. That is why doing c.l yields an error outside of the constructor - you never actually assigned to the object in the first place!

@martineau只是利用此行为来指出__dict__属性存在于self对象上,并为其分配:

@martineau simply took advantage of this behaviour to note that the __dict__ attribute exists on the self object, and assigns to it:

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)

之所以现在有效,是因为__dict__属性是Python在看到名称空间运算符.时需要查找方法签名或属性时调用的Python,并且还因为self尚未更改但仍引用对象实例.通过分配给self.__dict__,您可以获得原始类的几乎完全相同的副本(几乎完全",因为即使deepcopy也有限制).

This now works because the __dict__ attribute is what Python calls when Python needs to lookup a method signature or attribute when it sees the namespace operator ., and also because self has not been changed but still refers to the object instance. By assigning to self.__dict__, you obtain an almost exact copy of the original class ('almost exact' because even deepcopy has limits).

这个故事的寓意应该很明确:永远不要直接给self分配任何东西.相反,如果需要,仅 分配给self的属性. Python的元编程在这方面具有很大的灵活性,因此您应始终查阅文档.

The moral of the story should be clear: never assign anything to self directly. Instead, only assign to attributes of self if you ever need to. Python's metaprogramming permits a wide degree of flexibility in this regard, and you should always consult the documentation in this regard.

这篇关于以另一个类作为参数的类,复制行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆