我想这是那些无意中在python中创建引用的人提出的一长串问题之一,但我遇到了以下情况。我使用将数组顶部行的和设置为5(举个例子)。
class problem_test:
def __init__(self):
test_array = [[1,2,3,4,5,6,7],
[4,5,6,7,8,9,10]]
def set_top_row_to_five(x, array):
array[0] = array[0] + x
return abs(sum(array[0]) - 5)
adjustment = spo.minimize(set_top_row_to_five,0,args=(test_array))
print(test_array)
print(adjustment.x)
ptest = problem_test()
但是,优化正在改变原始数组(test_array
):
[array([-2.03, -1.03, -0.03, 0.97, 1.97, 2.97, 3.97]), [4, 5, 6, 7, 8, 9, 10]]
[-0.00000001]
我意识到我可以用深度拷贝来解决这个问题,但是我很想知道为什么会发生这种情况,这样以后我就不会意外地做同样的事情了。
提前感谢!
发布于 2018-02-05 00:00:44
名称是对对象的引用。要观察的是对象(也是在参数中传递的)是自己修改了还是创建了一个新的对象。一个例子是:
>>> l1 = list()
>>> l2 = l1
>>> l2.append(0) # this modifies object currently reference to by l1 and l2
>>> print(l1)
[0]
鉴于:
>>> l1 = list()
>>> l2 = list(l1) # New list object has been created with initial values from l1
>>> l2.append(0)
>>> print(l1)
[]
或者:
>>> l1 = list()
>>> l2 = l1
>>> l2 = [0] # New list object has been created and assigned to l2
>>> l2.append(0)
>>> print(l1)
[]
类似地假设l = [1, 2, 3]
>>> def f1(list_arg):
... return list_arg.reverse()
>>> print(f1, l)
None [3, 2, 1]
我们刚刚通过None
返回了我的list.reverse
方法,并反转了l
(就位)。然而:
>>> def f2(list_arg):
... ret_list = list(list_arg)
... ret_list.reverse()
... return ret_list
>>> print(f2(l), l)
[3, 2, 1] [1, 2, 3]
函数从l
返回一个新的反转对象(初始化),该对象保持不变(注意:在这个示例中,内置reversed
或切片更有意义)。
当嵌套时,不能忘记,例如:
>>> l = [1, 2, 3]
>>> d1 = {'k': l}
>>> d2 = dict(d1)
>>> d1 is d2
False
>>> d1['k'] is d2['k']
True
字典d1
和d2
是两个不同的对象,但它们的k
项只是一个(并且是共享的)实例。当copy.deepcopy
可能派上用场时,情况就是这样。
在传递对象时,需要小心,以确保它们被修改或复制按需要和预期使用。在进行更改时返回None
或类似的泛型值,并在处理副本时返回结果对象,这样函数/方法接口本身就可以提示意图是什么以及实际发生了什么。
当不可变对象(顾名思义)被“修改”时,将实际创建一个新对象,并将其分配给一个新对象或返回到原始名称/引用:
>>> s = 'abc'
>>> print('0x{:x} {}'.format(id(s), s))
0x7f4a9dbbfa78 abc
>>> s = s.upper()
>>> print('0x{:x} {}'.format(id(s), s))
0x7f4a9c989490 ABC
但是请注意,即使是不可变类型也可能包括对可变对象的引用。例如,对于l = [1, 2, 3]; t1 = (l,); t2 = t1
,可以使用t1[0].append(4)
。这种变化也会出现在t2[0]
中(由于与上面的d1['k']
和d2['k']
相同的原因),而这两个元组本身仍然没有修改。
一个额外的警告(可能会被抓到)。当定义默认参数值(使用可变类型)时,当函数被调用而不传递对象时,该默认参数的行为就像一个“静态”变量:
>>> def f3(arg_list=[]):
... arg_list.append('x')
... print(arg_list)
>>> f3()
['x']
>>> f3()
['x', 'x']
由于这通常不是人们乍看之下所采取的行为,因此通常最好避免将可变对象作为默认参数值。
类属性也是如此,其中一个对象将在所有实例之间共享:
>>> class C(object):
... a = []
... def m(self):
... self.a.append('x') # We actually modify value of an attribute of C
... print(self.a)
>>> c1 = C()
>>> c2 = C()
>>> c1.m()
['x']
>>> c2.m()
['x', 'x']
>>> c1.m()
['x', 'x', 'x']
请注意,在类似的示例中,在类不可变类型类属性的情况下,行为将是什么:
>>> class C(object):
... a = 0
... def m(self):
... self.a += 1 # We assign new object to an attribute of self
... print(self.a)
>>> c1 = C()
>>> c2 = C()
>>> c1.m()
1
>>> c2.m()
1
>>> c1.m()
2
所有有趣的细节都可以在文档中找到:https://docs.python.org/3.6/reference/datamodel.html
https://stackoverflow.com/questions/48555412
复制相似问题