Python:意外地创建了一个引用,但不确定h

2024-10-01 22:43:45 发布

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

我想这是那些无意中用python创建引用的人提出的一长串问题中的一个,但我有以下情况。我使用scipy minimize将数组顶行的和设置为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]

我意识到我可以用deepcopy来解决这个问题,但是我很想知道为什么会发生这种情况,这样我就不会在将来做同样的事情了。你知道吗

提前谢谢!你知道吗


Tags: totesttopdef情况数组arrayrow
1条回答
网友
1楼 · 发布于 2024-10-01 22:43:45

名称是对对象的引用。要观察的是对象(也是在参数中传递的)是自己修改的还是创建了一个新对象。例如:

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

字典d1d2是两个不同的对象,但它们的k项只是一个(和共享的)实例。这种情况下copy.deepcopy可能会派上用场。你知道吗

在传递对象时需要小心,以确保它们已被修改,或者按需要和预期使用了copy。在进行就地更改时返回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属性的行为:

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

相关问题 更多 >

    热门问题