先安利一个网站,对学习编程很有帮助:
http://www.pythontutor.com/

可以逐行可视化执行代码,具体自行体验啦


这个网站也是我在看别人的博文时候找到的,也先贴上别人的理解吧,我觉得写的都很好:

REF:

Python 对象引用、可变性和垃圾回收

python 深入理解 赋值、引用、拷贝、作用域


俗话说得好,师傅领进门,修行靠个人.学python也没多久,17年的时候走过一遍语法,应该没完.当时看的byte-of-python.

我觉得这书还行,轻量化,如果学过一门编程语言上手应该挺快的.最近又开始看视频学习了,看了几天,然而并没有实际写什么程序出来.

到视频后面感觉跟不上了,顿时明白自己应该叫停了.那些知识需要慢慢消化,不动手是不行的!

在用python的时候总感觉迷迷糊糊的,这次是解决引用赋值的问题.

上面两篇文章说的很清楚了,我就悄悄把他们剪切一下,方便自己理解:

可变对象和不可变对象

在Python中,对象分为两种:可变对象和不可变对象,不可变对象包括int,float,long,str,tuple等,可变对象包括list,set,dict等。需要注意的是:这里说的不可变指的是值的不可变。对于不可变类型的变量,如果要更改变量,则会创建一个新值,把变量绑定到新值上,而旧值如果没有被引用就等待垃圾回收。另外,不可变的类型可以计算hash值,作为字典的key。可变类型数据对对象操作的时候,不需要再在其他地方申请内存,只需要在此对象后面连续申请(+/-)即可,也就是它的内存地址会保持不变,但区域会变长或者变短。

示例:

 #immutable
a = 12
print(id(a))
a=1
print(id(a))
st = "thisisastring"
print(id(st))
st = "string"
print(id(st)) #mutable
li = [12,12,342,4]
print(id(li))
li.append(12)
print(id(li)) #a new list
li = [12,123,213,123,123]
print(id(li))
10943360
10943008
140417189762736
140417373204184
140417198413960
140417198413960
140417198537224

可以看出,当对象不可变时,重新赋值,地址已经改变了,也就是重新绑定"贴"到新的值上去了.

当对象可变时,比如上例中的list,追加(append)一个元素后,地址并没变,只是在原来的基础上增加一个元素.

下面那句

li = [12,123,213,123,123]

其实是重新创建了一个list,然后把li指向它,所以地址显然改变了.

赋值和引用

python中的变量名应该理解为标签,就是常说的引用.在赋值的时候总是把对象的引用值传给变量.

a=12
b=a
print(id(a),id(b))
b=13
print(id(a),id(b))
10943360 10943360
10943360 10943392

当对象不可变的时候,给已有变量赋值就会创建新对象,然后传新引用值给变量.而以往接触的程序语言总是地址不变,而改变其内的值.

起初b=a,b获得了a的引用,所以ab的地址相同,而给b赋新值时,重新创建了对象,然后把b贴上去了,所以之后ab的地址不同.

 mylist= [12,13,1234]
anotherlist = mylist
anotherlist.append(999)
print(mylist,id(mylist),id(anotherlist))
del mylist[0]
print(anotherlist,mylist) del anotherlist
print(mylist,id(mylist))
 [12, 13, 1234, 999] 140417198414536 140417198414536
[13, 1234, 999] [13, 1234, 999]
[13, 1234, 999] 140417198414536

当对象可变时,直接把一个list赋值给另一个list,传给那个新list的其实也是引用.

所以mylist和anotherlist其实两者指向的是一个对象,

然后给anotherlist追加一个元素,再删除首元素,由于指向一个对象,所以输出一样.

最后删除anotherlist只是删除了这个标签而已,因为那个他们指向的对象还有mylist使用.(恩,这个网站确实有助于理解)

拷贝

那么究竟如何将一个对象像以前学的如c语言那样赋值是创建的独立的对(bian)象(liang)呢?

在python中这中操作叫拷贝,拷贝又分两种,浅复制(copy),深复制(deepcopy).

上面说了,直接赋值得到的是引用,要想拷贝就得使用别的操作,常见的有list的切片,list()方法,copy()方法.

alist = [12,12,12,23,23213]
blist = alist[:]
clist = list(alist)
from copy import copy
dlist = copy(alist)
print(alist==blist==clist)
print(id(alist),id(blist),id(clist))
alist.append(77)
print(alist==blist==clist)
print(id(alist),id(blist),id(clist))

True
140417198414216 140417198609096 140417189752712
False
140417198414216 140417198609096 140417189752712

对象相等但是地址不同,说明确实是创建了新对象,之后给alist追加一个元素,三者不等.


==和is

这里插播一下两者的区别,

x = [1, 2, 3]
y = [1, 2, 3]
print(x == y)
print(x is y)
print(id(x))
print(id(y)) 执行结果:
True
False
2194144817928
2194144817288

==比较的是对象是否相等,is 比较的是引用是否相等,x和y的对象都是1,2,3但是他们其实是不同的对象,只是值相同,然后x和y则是指向相等的不同的对象的不同的引用了.


为什么说是浅拷贝呢?

 alist = [12,12,12,[12,23],23213]
blist = alist[:]
blist.append(777)
blist[3][0]=21
alist[3][1]=32
print(alist,blist)
[12, 12, 12, [21, 32], 23213] [12, 12, 12, [21, 32], 23213, 777]

从输出的结果看到,无论是从alist中去更改alist[3]还是blist去更改blist[3],结果alist和blist的3号元素都改变了,这样的拷贝就叫浅拷贝,它只拷贝了最外面那层.

当然看图片更容易懂了:

从图片可以看到其它元素都是重新复制创建了一份,但是3号元素还是只是一个引用,和原来alist中那个引用相同.所以任意一个list去修改3号元素另一方会跟着改变.


append和extend

再插播一下这两者的区别:

 alist = [12,12,12,[12,23],23213]
blist = list(alist)
alist.append([12,777])
blist.extend([12,777])
print(alist)
print(blist)
[12, 12, 12, [12, 23], 23213, [12, 777]]
[12, 12, 12, [12, 23], 23213, 12, 777]

看输出就明白不同啦

附:

Help on method_descriptor:

append(...)
L.append(object) -> None -- append object to end Help on method_descriptor: extend(...)
L.extend(iterable) -> None -- extend list by appending elements from the iterable

再探深拷贝:

 alist = [12,12,[12,23],23213]
blist = alist[:]
from copy import deepcopy
clist = deepcopy(alist)
print(alist==blist==clist)
print(id(alist),id(blist),id(clist))
print(id(alist[2]),id(blist[2]),id(clist[2]))
alist[2][0]=777
print((alist),(blist),(clist))
True
140417189861576 140417189752008 140417189860744
140417198413960 140417198413960 140417198612360
[12, 12, [777, 23], 23213] [12, 12, [777, 23], 23213] [12, 12, [12, 23], 23213]

blist由list()方法浅拷贝得来,clist由alist深拷贝而来,输出的第三行地址可见对于2号元素(一个子list)的地址,alist,blist相同,而clist和他们不同,是重新创建的.

最后打印的结果也说明浅拷贝只是拷贝了外面那层,对于2号元素还是相同的引用.

未完待续......

Python引用拷贝赋值的更多相关文章

  1. python引用方法赋值问题探究

    python脚本编写中,经常会遇到引用一个模块的方法的场景.引用的方法里到底赋不赋值曾经困扰了我好久. 最近利用python写了一个接口自动化测试脚本,在查阅观看多篇博文和视频后解决了封装方法引用的问 ...

  2. python 深入理解 赋值、引用、拷贝、作用域

    在 python 中赋值语句总是建立对象的引用值,而不是复制对象.因此,python 变量更像是指针,而不是数据存储区域, 这点和大多数 OO 语言类似吧,比如 C++.java 等 ~ 1.先来看个 ...

  3. Python中的赋值和拷贝

    赋值 在python中,赋值就是建立一个对象的引用,而不是将对象存储为另一个副本.比如: >>> a=[1,2,3] >>> b=a >>> c= ...

  4. C++ 为什么拷贝构造函数参数必须为引用?赋值构造函数参数也必须为引用吗?

    之前写拷贝构造函数的时候,以为参数为引用,不为值传递,仅仅是为了减少一次内存拷贝.然而今天看到一篇文章发现自己对拷贝构造的参数理解有误. 参数为引用,不为值传递是为了防止拷贝构造函数的无限递归,最终导 ...

  5. python中的“赋值与深浅拷贝”

    Python中,赋值与拷贝(深/浅拷贝)之间是有差异的,这主要源于数据在内存中的存放问题,本文将对此加以探讨. 1 赋值(添加名字) 赋值不会改变内存中数据存放状态,比如在内存中存在一个名为data的 ...

  6. Python开发【第二章】:Python深浅拷贝剖析

    Python深浅拷贝剖析 Python中,对象的赋值,拷贝(深/浅拷贝)之间是有差异的,如果使用的时候不注意,就可能产生意外的结果. 下面本文就通过简单的例子介绍一下这些概念之间的差别. 一.对象赋值 ...

  7. python引用和对象详解

    python引用和对象详解 @[马克飞象] python中变量名和对象是分离的 例子 1: a = 1 这是一个简单的赋值语句,整数 1 为一个对象,a 是一个引用,利用赋值语句,引用a指向了对象1. ...

  8. python中的赋值操作和复制操作

    之前一直写C#,变量之间赋值相当于拷贝,修改拷贝变量不会改变原来的值.但是在python中发现赋值操作本质是和C++中的引用类似,即指向同一块内存空间.下面通过一个例子说明: p=[0,1,2,3,4 ...

  9. python中拷贝对象的区别

    一.赋值.引用 在python中赋值语句总是建立对象的引用值,而不是复制对象.因此,python变量更像是指针,而不是数据存储区域 这点和大多数语音类似吧,比如C++.Java等 1.先看个例子: v ...

随机推荐

  1. window alias给cmd命令起别名

    场景: Linux的alias命令是个非常实用的工具,任何命令通过alias可以精简到很短,比如:alias l='ls -l' Windows也有alias类似的命令,就是:doskey,开启方法也 ...

  2. [原]Django(1)----Django-setting中的STATIC_URL 和STATIC_ROOT 和STATICFILES_DIRS 的区别

    1)对比以下两行图 理解STATIC_URL的意义 #access static files by url STATIC_URL = '/static/' 2)部署django项目的时候需要用到STA ...

  3. flask使用pymysql连接MySQL,生成xls文件并下载到本地

    版本一:将MySQL数据写入到excel(xsl)文件并下载到默认文件夹(一般问电脑的下载文件夹里面),并显示特效到前端页面. flask框架连接MySQL,我们使用pymsql这个工具,如下操作: ...

  4. 一次CTF后对二维码的认识

    前一段时间参加一个CTF比赛的时候其中有一个题目就是一张二维码图片,然后获取其中的信息来解题,那个二维码的特别之处在于,它把3个位置探测区域用几张美女图片代替了,然后在做题的时候顺便简单的了解了一下二 ...

  5. web 接口测试入门

    在此之前先简单的介绍一下基本概念:我们想要打开一个网站,首先是需要往浏览器的地址的URL输入框架中输入网地址.当我敲下回车后,通过HTTP协议,将网址传送到域名解析服务器,域名解析服务器根据网址找到对 ...

  6. hibernate08--OpenSessionInView

    创建一个web项目,然后生成HibernateSessionFactory文件! package cn.bdqn.util; import org.hibernate.HibernateExcepti ...

  7. 方差+标准差+四分位数+z-score公式

    一.方差公式 $S^2 = \frac{1}{N}\sum_{i=1}^{N}(X_i - \mu)^2 = \frac{1}{N}[(X_1-\mu)^2 + (X_2-\mu)^2 + ... + ...

  8. 补充:MySQL整理

    1.连接Mysql 格式: mysql -h主机地址 -u用户名 -p用户密码 1.连接到本机上的MYSQL.首先打开DOS窗口,然后进入目录mysql\bin,再键入命令mysql -u root ...

  9. JavaScript基础知识(数组的方法)

    数组的方法(15个) 对象数据类型: 数组成员有一个与之对应的索引 length : 代表数组成员的个数: 操作改变数组一些方法:这些数组的方法都是内置的: // 1. 方法作用: // 2. 方法的 ...

  10. Markdown编辑工具及命令

    Markdown是一种可以使用普通文本编辑器编辑的标记语言,通过使用简单的编辑,可以使文本具有一定的格式. Typora是一款简介的Markerdown编辑器. 文本编辑语法: 标题: # 一阶标题 ...