首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Python:意外创建了一个引用,但不确定是如何创建的

Python:意外创建了一个引用,但不确定是如何创建的
EN

Stack Overflow用户
提问于 2018-02-01 04:35:18
回答 1查看 152关注 0票数 0

我想这是那些无意中在python中创建引用的人提出的一长串问题之一,但我遇到了以下情况。我使用将数组顶部行的和设置为5(举个例子)。

代码语言:javascript
运行
复制
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):

代码语言:javascript
运行
复制
[array([-2.03, -1.03, -0.03,  0.97,  1.97,  2.97,  3.97]), [4, 5, 6, 7, 8, 9, 10]]
[-0.00000001]

我意识到我可以用深度拷贝来解决这个问题,但是我很想知道为什么会发生这种情况,这样以后我就不会意外地做同样的事情了。

提前感谢!

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-02-05 00:00:44

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

代码语言:javascript
运行
复制
>>> l1 = list()
>>> l2 = l1
>>> l2.append(0)  # this modifies object currently reference to by l1 and l2
>>> print(l1)
[0]

鉴于:

代码语言:javascript
运行
复制
>>> l1 = list()
>>> l2 = list(l1)  # New list object has been created with initial values from l1
>>> l2.append(0)
>>> print(l1)
[]

或者:

代码语言:javascript
运行
复制
>>> 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]

代码语言:javascript
运行
复制
>>> def f1(list_arg):
...    return list_arg.reverse()
>>> print(f1, l)
None [3, 2, 1]

我们刚刚通过None返回了我的list.reverse方法,并反转了l (就位)。然而:

代码语言:javascript
运行
复制
>>> 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或切片更有意义)。

当嵌套时,不能忘记,例如:

代码语言:javascript
运行
复制
>>> l = [1, 2, 3]
>>> d1 = {'k': l}
>>> d2 = dict(d1)
>>> d1 is d2
False
>>> d1['k'] is d2['k']
True

字典d1d2是两个不同的对象,但它们的k项只是一个(并且是共享的)实例。当copy.deepcopy可能派上用场时,情况就是这样。

在传递对象时,需要小心,以确保它们被修改或复制按需要和预期使用。在进行更改时返回None或类似的泛型值,并在处理副本时返回结果对象,这样函数/方法接口本身就可以提示意图是什么以及实际发生了什么。

当不可变对象(顾名思义)被“修改”时,将实际创建一个新对象,并将其分配给一个新对象或返回到原始名称/引用:

代码语言:javascript
运行
复制
>>> 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']相同的原因),而这两个元组本身仍然没有修改。

一个额外的警告(可能会被抓到)。当定义默认参数值(使用可变类型)时,当函数被调用而不传递对象时,该默认参数的行为就像一个“静态”变量:

代码语言:javascript
运行
复制
>>> def f3(arg_list=[]):
...     arg_list.append('x')
...     print(arg_list)
>>> f3()
['x']
>>> f3()
['x', 'x']

由于这通常不是人们乍看之下所采取的行为,因此通常最好避免将可变对象作为默认参数值。

类属性也是如此,其中一个对象将在所有实例之间共享:

代码语言:javascript
运行
复制
>>> 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']

请注意,在类似的示例中,在类不可变类型类属性的情况下,行为将是什么:

代码语言:javascript
运行
复制
>>> 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

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48555412

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档