以另一个类作为参数的类,复制行为 [英] Class that takes another class as argument, copies behavior
问题描述
我想在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屋!