Copy and delve into issues scoping of variables in Python

This article describes the scope of the problem and a copy of variables in Python, including some assignments, references, and related functions between different versions of Python2 and 3, need friends can refer to the next
assignment is always based in python reference value of the object, rather than copying objects. Thus, like Python is a pointer variable, rather than the data storage area, Here Insert Picture Description
this is most similar to it OO languages, such as ~ C ++, java like
1, a first look all right:

In Python, so values ​​= [0,1,2]; values ​​[1] = values, why the result is [0, [...], 2]?

>>> values = [0, 1, 2]
>>> values[1] = values
>>> values
[0, [...], 2]

I should be expected

[0, [0, 1, 2], 2]

But the result is what you want to assign unlimited?

Python can be said that has no value, only by reference. In this way you create the equivalent of a reference to its own structure, resulting in an infinite loop. To understand this problem, there is a need to understand the basic concepts.

Python is no "variable", we usually variable is only "label" is a reference.

carried out

values = [0, 1, 2]

When, Python thing to do is to first create a list of objects [0, 1, 2], then give it a label named values. If subsequently executed

values = [3, 4, 5]

Then, Python thing to do is to create another list of objects [3, 4, 5], and then just goes from the front label called values ​​of [0, 1, 2] on the object torn, re-posted to [3 , 4, 5] this object.

Start to finish, and not a named list of values object container exist, Python did not put any value of the object to be copied into values. As shown in FIG procedure: Here Insert Picture Description
performing

 values[1] = values

的时候,Python 做的事情则是把 values 这个标签所引用的列表对象的第二个元素指向 values 所引用的列表对象本身。执行完毕后,values 标签还是指向原来那个对象,只不过那个对象的结构发生了变化,从之前的列表 [0, 1, 2] 变成了 [0, ?, 2],而这个 ? 则是指向那个对象本身的一个引用。如图所示:Here Insert Picture Description
要达到你所需要的效果,即得到 [0, [0, 1, 2], 2] 这个对象,你不能直接将 values[1] 指向 values 引用的对象本身,而是需要吧 [0, 1, 2] 这个对象「复制」一遍,得到一个新对象,再将 values[1] 指向这个复制后的对象。Python 里面复制对象的操作因对象类型而异,复制列表 values 的操作是

values[:] #生成对象的拷贝或者是复制序列,不再是引用和共享变量,但此法只能顶层复制

所以你需要执行

values[1] = values[:]

Python 做的事情是,先 dereference 得到 values 所指向的对象 [0, 1, 2],然后执行 [0, 1, 2][:] 复制操作得到一个新的对象,内容也是 [0, 1, 2],然后将 values 所指向的列表对象的第二个元素指向这个复制二来的列表对象,最终 values 指向的对象是 [0, [0, 1, 2], 2]。过程如图所示: Here Insert Picture Description
往更深处说,values[:] 复制操作是所谓的「浅复制」(shallow copy),当列表对象有嵌套的时候也会产生出乎意料的错误,比如

a = [0, [1, 2], 3]
b = a[:]
a[0] = 8
a[1][1] = 9

问:此时 a 和 b 分别是多少?

正确答案是 a 为 [8, [1, 9], 3],b 为 [0, [1, 9], 3]。发现没?b 的第二个元素也被改变了。想想是为什么?不明白的话看下图 Here Insert Picture Description
正确的复制嵌套元素的方法是进行「深复制」(deep copy),方法是

import copy
  
a = [0, [1, 2], 3]
b = copy.deepcopy(a)
a[0] = 8
a[1][1] = 9

Here Insert Picture Description
2、引用 VS 拷贝:

(1)没有限制条件的分片表达式(L[:])能够复制序列,但此法只能浅层复制。

(2)字典 copy 方法,D.copy() 能够复制字典,但此法只能浅层复制

(3)有些内置函数,例如 list,能够生成拷贝 list(L)

(4)copy 标准库模块能够生成完整拷贝:deepcopy 本质上是递归 copy

(5)对于不可变对象和可变对象来说,浅复制都是复制的引用,只是因为复制不变对象和复制不变对象的引用是等效的(因为对象不可变,当改变时会新建对象重新赋值)。所以看起来浅复制只复制不可变对象(整数,实数,字符串等),对于可变对象,浅复制其实是创建了一个对于该对象的引用,也就是说只是给同一个对象贴上了另一个标签而已。

L = [1, 2, 3]
D = {'a':1, 'b':2}
A = L[:]
B = D.copy()
print "L, D"
print L, D
print "A, B"
print A, B
print "--------------------"
A[1] = 'NI'
B['c'] = 'spam'
print "L, D"
print L, D
print "A, B"
print A, B
  
  
L, D
[1, 2, 3] {'a': 1, 'b': 2}
A, B
[1, 2, 3] {'a': 1, 'b': 2}
--------------------
L, D
[1, 2, 3] {'a': 1, 'b': 2}
A, B
[1, 'NI', 3] {'a': 1, 'c': 'spam', 'b': 2}

3、增强赋值以及共享引用:

x = x + y,x 出现两次,必须执行两次,性能不好,合并必须新建对象 x,然后复制两个列表合并

属于复制/拷贝

x += y,x 只出现一次,也只会计算一次,性能好,不生成新对象,只在内存块末尾增加元素。

当 x、y 为list时, += 会自动调用 extend 方法进行合并运算,in-place change。

属于共享引用

L = [1, 2]
M = L
L = L + [3, 4]
print L, M
print "-------------------"
L = [1, 2]
M = L
L += [3, 4]
print L, M
  
  
[1, 2, 3, 4] [1, 2]
-------------------
[1, 2, 3, 4] [1, 2, 3, 4]

4、python 从2.x 到3.x,语句变函数引发的变量作用域问题

先看段代码:

def test():
  a = False
  exec ("a = True")
  print ("a = ", a)
test()
  
b = False
exec ("b = True")
print ("b = ", b)

在 python 2.x 和 3.x 下 你会发现他们的结果不一样:

2.x:
a = True
b = True
  
3.x:
a = False
b = True

这是为什么呢?

因为 3.x 中 exec 由语句变成函数了,而在函数中变量默认都是局部的,也就是说

你所见到的两个 a,是两个不同的变量,分别处于不同的命名空间中,而不会冲突。
知道原因了,我们可以这么改改:

 def test():
  a = False
  ldict = locals()
  exec("a=True",globals(),ldict)
  a = ldict['a']
  print(a)
  
test()
  
b = False
exec("b = True", globals())
print("b = ", b)

Stackoverflow on this issue has already been asked, but it was also reported that the official python bug
recommend our learning Python buckle qun: 774711191, look at how seniors are learning! From basic web development python script to, reptiles, django, data mining, etc. [PDF, actual source code], zero-based projects to combat data are finishing. Given to every little python partner! Every day, Daniel explain the timing Python technology, to share some of the ways to learn and need to pay attention to small details, click on Join us python learner gathering

Published 16 original articles · won praise 10 · views 10000 +

Guess you like

Origin blog.csdn.net/haoxun07/article/details/104486306