问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

深入探究Python中变量的拷贝和作用域问题

发布网友 发布时间:2022-03-26 02:18

我来回答

2个回答

懂视网 时间:2022-03-26 06:39

在Python程序中创建、改变、查找变量名时,都是在一个保存变量名的空间中进行,而这个空间被称之为作用域。

  

  Python是一种跨平台的计算机程序设计语言。是一种面向对象的动态类型语言,最初被设计用于编写自动化脚本shell,随着版本的不断更新和语言新功能的添加,越来越多被用于独立的、大型项目的开发。自从20世纪90年代初Python语言诞生至今,它已被逐渐广泛应用于系统管理任务的处理和Web编程。

  

  

热心网友 时间:2022-03-26 03:47

  这篇文章主要介绍了Python中变量的拷贝和作用域问题,包括一些赋值、引用问题,以及相关函数在Python2和3版本之间的不同,需要的朋友可以参考下
  在
python
中赋值语句总是建立对象的引用值,而不是复制对象。因此,python
变量更像是指针,而不是数据存储区域,
  这点和大多数
OO
语言类似吧,比如
C++、java

~
  1、先来看个问题吧:
  在Python中,令values=[0,1,2];values[1]=values,为何结果是[0,[...],2]?
  ?
1
2
3
4
>>>
values
=
[0,
1,
2]
>>>
values[1]
=
values
>>>
values
[0,
[...],
2]
  我预想应当是
  ?
1
[0,
[0,
1,
2],
2]
  但结果却为何要赋值无限次?
  可以说
Python
没有赋值,只有引用。你这样相当于创建了一个引用自身的结构,所以导致了无限循环。为了理解这个问题,有个基本概念需要搞清楚。
  Python
没有「变量」,我们平时所说的变量其实只是「标签」,是引用。
  执行
  ?
1
values
=
[0,
1,
2]
  的时候,Python
做的事情是首先创建一个列表对象
[0,
1,
2],然后给它贴上名为
values
的标签。如果随后又执行
  ?
1
values
=
[3,
4,
5]
  的话,Python
做的事情是创建另一个列表对象
[3,
4,
5],然后把刚才那张名为
values
的标签从前面的
[0,
1,
2]
对象上撕下来,重新贴到
[3,
4,
5]
这个对象上。
  至始至终,并没有一个叫做
values
的列表对象容器存在,Python
也没有把任何对象的值复制进
values
去。过程如图所示:
  执行
  ?
1
values[1]
=
values
  的时候,Python
做的事情则是把
values
这个标签所引用的列表对象的第二个元素指向
values
所引用的列表对象本身。执行完毕后,values
标签还是指向原来那个对象,只不过那个对象的结构发生了变化,从之前的列表
[0,
1,
2]
变成了
[0,
?,
2],而这个
?
则是指向那个对象本身的一个引用。如图所示:
  要达到你所需要的效果,即得到
[0,
[0,
1,
2],
2]
这个对象,你不能直接将
values[1]
指向
values
引用的对象本身,而是需要吧
[0,
1,
2]
这个对象「复制」一遍,得到一个新对象,再将
values[1]
指向这个复制后的对象。Python
里面复制对象的操作因对象类型而异,复制列表
values
的操作是
  values[:]
#生成对象的拷贝或者是复制序列,不再是引用和共享变量,但此法只能顶层复制
  所以你需要执行
  ?
1
values[1]
=
values[:]
  Python
做的事情是,先
dereference
得到
values
所指向的对象
[0,
1,
2],然后执行
[0,
1,
2][:]
复制操作得到一个新的对象,内容也是
[0,
1,
2],然后将
values
所指向的列表对象的第二个元素指向这个复制二来的列表对象,最终
values
指向的对象是
[0,
[0,
1,
2],
2]。过程如图所示:
  往更深处说,values[:]
复制操作是所谓的「浅复制」(shallow
copy),当列表对象有嵌套的时候也会产生出乎意料的错误,比如
  ?
1
2
3
4
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
的第二个元素也被改变了。想想是为什么?不明白的话看下图
  正确的复制嵌套元素的方法是进行「深复制」(deep
copy),方法是
  ?
1
2
3
4
5
6
import
copy

a
=
[0,
[1,
2],
3]
b
=
copy.deepcopy(a)
a[0]
=
8
a[1][1]
=
9
  2、引用
VS
拷贝:
  (1)没有*条件的分片表达式(L[:])能够复制序列,但此法只能浅层复制。
  (2)字典
copy
方法,D.copy()
能够复制字典,但此法只能浅层复制
  (3)有些内置函数,例如
list,能够生成拷贝
list(L)
  (4)copy
标准库模块能够生成完整拷贝:deepcopy
本质上是递归
copy
  (5)对于不可变对象和可变对象来说,浅复制都是复制的引用,只是因为复制不变对象和复制不变对象的引用是等效的(因为对象不可变,当改变时会新建对象重新赋值)。所以看起来浅复制只复制不可变对象(整数,实数,字符串等),对于可变对象,浅复制其实是创建了一个对于该对象的引用,也就是说只是给同一个对象贴上了另一个标签而已。
  ?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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。
  属于共享引用
  ?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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,语句变函数引发的变量作用域问题
  先看段代码:
  ?
1
2
3
4
5
6
7
8
9
def
test():
a
=
False
exec
("a
=
True")
print
("a
=
",
a)
test()

b
=
False
exec
("b
=
True")
print
("b
=
",
b)
  在
python
2.x

3.x

你会发现他们的结果不一样:
  ?
1
2
3
4
5
6
7
2.x:
a
=
True
b
=
True

3.x:
a
=
False
b
=
True
  这是为什么呢?
  因为
3.x

exec
由语句变成函数了,而在函数中变量默认都是局部的,也就是说
  你所见到的两个
a,是两个不同的变量,分别处于不同的命名空间中,而不会冲突。
  具体参考
《learning
python》P331-P332
  知道原因了,我们可以这么改改:
  ?
1
2
3
4
5
6
7
8
9
10
11
12
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
上已经有人问了,而且
python
官方也有人报了
bug。。。
  具体链接在下面:
  http://stackoverflow.com/questions/7668724/variables-declared-in-execed-code-dont-become-local-in-python-3-documentatio
  http://bugs.python.org/issue4831
  http://stackoverflow.com/questions/1463306/how-does-exec-work-with-locals
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
临沂比较有名的男装品牌 呼伦贝尔市悦动网络科技有限公司怎么样? 呼伦贝尔中汇实业有限公司怎么样? 呼伦贝尔油玉不绝电子商务有限公司怎么样? 如何避免wps卡顿? 属鼠的男人找对象是属什么,属鼠的人和什么属相合 96年鼠的姻缘在哪年 属相相合年份运势提升 2024属鼠找对象属什么最佳 黑客攻击网站能报案吗 黑客攻击报案有用吗 python变量的作用域到底怎么理解怎么用呢?谢谢! python什么是闭包 闭包的作用域 PYTHON 的变量作用域与内存分配 Python 中作用域与命名空间的问题? Python 的类中到底有没有建立作用域 Python语言中作用域怎么理解? 苹果智能手机与其它品牌手机使用的实际优势在哪里? 苹果手机的优势是什么啊? 苹果手机的优点是什么? IPHONE手机有什么优点?它的好处到底在哪里? iPhone手机的优势究竟在哪里? 苹果手机比其他手机究竟好在哪里 苹果手机有什么优点?有什么缺点? 苹果公布业绩,苹果有手机有何优势? iPhone到底好用在哪里?为什么很多人都在买苹果手机? 苹果手机的好处在哪里 苹果手机那么贵,为什么还有那么多人用?有什么优势吗? 苹果手机有哪些好处? 苹果手机有什么优缺点? 苹果手机的有哪些优点。 python 2.7 变量作用域的疑问 python中什么叫局部作用域 python for循环作用域问题 初学python,有关函数作用域问题 python 程序中有多少全局作用域?有多少局部作用域 如何在特定的作用域将python系统函数替换成自己的函数 python函数实参不是的作用域问题,高手来解释下下面的输出? 没看python的基础,求教python变量的作用域问题,下面附代码,感谢! python global的作用域 python中global的作用是什么? python3.5中,无法numpy怎么解决 dra-tl00什么型号? dra-tl00可以无线充电吗? dra-tl00能不能用三网的? 联通短信中心号码怎么设置? 苹果手机里不等于符号怎么打出来? 不等于的符号怎么打?打出来 不等于号怎么打出来? 请问不等于的符号是怎么输入进去的? 不等于号怎么打出来