Python进阶之一_变量的地址、id内置函数、深浅拷贝

注意:以下为个人理解,只为个人能直观理解和记忆,不保证完全的正确性

一 “python中一切都是对象”,这句话怎么理解?

https://blog.csdn.net/fragmentalice/article/details/81363494

可以从数据的角度看,整型数值1、浮点型数值1.0、字符串’1’、元组(1,1)、字典{‘a’:1}、列表[1]、集合{1}、空值None,这些数据在python中称为对象,存储在内存中。
其中,整数数值、浮点型数值、字符串、元组,对应的内存单元中的值不能改变,将这些对象称为不可变对象。而字典、列表、集合,对应的内存单元中的值能够改变,将这些对象称为可变对象。
x=1、x=1.0、x=‘1’、x=(1,1)、x={‘a’:1}、x=[1]、x={1},这些表达式中,x称为变量,或者说是对象的引用。
注意:可变对象list、dict、set中的元素是引用变量而非实际对象,由引用变量继续指向可变或不可变对象,明确这点才能理解浅拷贝。【这么指来指去,可变对象只不过是不可变对象的引用变量的集合,最终都是指向不可变对象了】

个人理解:“python中一切都是对象”,这句话应该是"python中一切数据都是对象",且要补充上"一切变量都是引用";由于变量都是引用类型,比较统一,所以变量不需要指明数据类型,list中元素也能是不同的数据类型【因为list中实际装的还是引用】。

二 对象的地址

内置函数id()能够查看对象的地址,通过id(对象)、id(对象的引用)的方式,都可以返回对象的地址

不可变对象:
不可变对象的值与地址一一对应,即整个内存中,为该值的内存单元只有这一块,且这一块内存单元的值始终不会变,是常量;所有指向该值的变量,指向的都是这个对象。如:

a=1
b=int(1.0)
c=int('1')
d=(1,1)[0]
e={'x':1}['x']
f=[1][0]
g={1}.pop()

以上所有变量指向的值均为整型数值1,指向的地址均相同

当变量参与运算后要改变变量(指向的对象)的值时,并没有改变对象的值,而是将对象的值复制一份并运算得到一个新的对象,然后将变量指向新的对象

x=1
print(id(x))

x=x+1
print(id(x))	# 此时x指向整数数值2这个对象了

x指向不同的地址

可变对象:
可变对象的值与内存地址之间没有固定关系,不同地址中的可变对象可以有相同的值,相同的值可以对应不同的对象。

id([1])
id([1])
第一个[1]和第二个[1]是两个对象,位于不同的内存中 

x=[1]
y=[1]
x、y指向不同的地址

当变量参与运算后要改变可变对象的值时,可变对象不需要复制,直接在原对象上修改。

x=[1]
id(x)
x.append(2)
id(x)

x指向同一个地址

三 直接赋值、浅拷贝、深拷贝

https://www.runoob.com/w3cnote/python-understanding-dict-copy-shallow-or-deep.html
https://blog.csdn.net/fragmentalice/article/details/81363494

这里copy、deepcopy是copy模块中的函数,需要import copy;list、dict、set的copy函数同copy模块中copy函数,都是浅拷贝

1 直接赋值、浅拷贝、深拷贝:
可变对象list、dict、set中的元素是引用变量而非实际对象,由引用变量指向最终的可变或不可变对象。所以对于list,如a=[1],实际上是一个嵌套的引用变量,引用变量a指向内存单元1,内存单元1中存有引用变量a[0],引用变量a[0]指向不可变对象整型数值1

直接赋值: 直接将父对象的引用赋值给新的变量,新旧变量指向同一个父对象;所以a,b指向同一个对象;
浅拷贝: 在内存中复制父对象的内存单元,也即父对象中的引用元素被复制了一份,但最终指向的对象不复制;
深拷贝: 在内存中复制父对象的内存单元,递归复制父对象、子孙对象中引用元素指向的可变对象的内存单元;

浅拷贝复制到父对象中引用变量这一级(级1),深拷贝一直复制到不可变对象上一级(级2);只有可变对象中嵌套可变对象(级1不等于级2),浅拷贝、深拷贝的区别才会表现出来;如果可变对象中引用本来全部指向不可变对象了(级1等于级2),如a=[1,2,3],这时浅拷贝、深拷贝没什么区别。

在这里插入图片描述
2 对象引用计数:
python内存中每个对象都维护一个引用计数器用于垃圾回收、释放内存。当对象被引用时计数器加1,当计数器值为0时,解释器会释放对象所占的内存。

sys.getrefcount()可以获取对象的引用次数,参数为对象或对象的引用
注意:获取的是整个python程序对该变量的引用次数,包括python底层模块中使用的,并非只是用户自己模块中引用的次数

del关键字可以手动删除引用变量,使计数器减1。

a=1
b=[1]
c=b
print(sys.getrefcount(1))		# 183,参数为不可变对象1
print(sys.getrefcount(a))		# 183,参数为不可变对象1的引用a
print(sys.getrefcount([1]))		# 1,参数为可变对象[1]
print(sys.getrefcount(b))		# 3,参数为另一个可变对象[1]的引用b
print(sys.getrefcount(c))		# 3,参数为上一个可变对象[1]的引用c

del a							# 删除引用变量a
print(sys.getrefcount(1))		# 182

结果分析:
b指向的可变对象的引用次数是3次,而不是2次;说明python底层在等号左边写[1]时,就已经创建了一个引用变量了
【能不能理解为:1这个写法本来就是对内存中值为1的对象的引用,1本来就是一个引用变量的变量名,只不过这个变量名很特殊,而这个变量指向的内存单元的值是预设的;
哈哈,如果这么想的话,"对象"只是一个概念,对应于内存中的一块内存单元,用户写的所有变量、常量都是对这个"对象"的表述、引用;
这么想的话,不存在“函数的参数为引用或对象"这种说法了,其实都只是“引用”;a和1都是引用】
补充:看一下字面值常量的概念

3 对象相等: is、==
is判断两个引用变量指向的是否为同一个内存地址,==判断两个引用变量最终指向的对象的值是否相等

a=1
b=1
print(a is b)		# True
print(a==b)			# True

c=[1]
d=[1]
print(c is d)		# False
print(c==d)			# True

四 举例

# 可变对象、不可变对象的地址
x=1
print(id(x))		# 1549658336	整型数值1的地址
print(id(1))		# 1549658336	整型数值1的地址

y=[1]
print(id(y[0]))		# 1549658336	整型数值1的地址
print(id(y))		# 53427736		列表[1]的地址
print(id([1]))		# 53452432		另一个列表[1]的地址

# 直接赋值、浅拷贝、深拷贝
ls=[[1],2,3]
ls1=ls					# 直接赋值
ls2=copy.copy(ls)		# 浅拷贝
ls3=copy.deepcopy(ls)	# 深拷贝

print('第一组:---------------------------')
print(id(ls))			# 58539664
print(id(ls1))			# 58539664
print(id(ls2))			# 58539824
print(id(ls3))			# 58400320
print('第二组:---------------------------')
print(id(ls[0]))		# 58398800
print(id(ls1[0]))		# 58398800
print(id(ls2[0]))		# 58398800
print(id(ls3[0]))		# 58539584
print('第三组:---------------------------')
print(id(ls[1]))		# 1558178032
print(id(ls1[1]))		# 1558178032
print(id(ls2[1]))		# 1558178032
print(id(ls3[1]))		# 1558178032
print('第四组:---------------------------')
print(id(ls[0][0]))		# 1558178016
print(id(ls1[0][0]))	# 1558178016
print(id(ls2[0][0]))	# 1558178016
print(id(ls3[0][0]))	# 1558178016

直接赋值、浅拷贝、深拷贝结果分析:
第一组输出:

直接赋值的ls1指向的还是原对象,所以ls1和ls指向的地址相同
浅拷贝、深拷贝都重新复制了父对象到新的内存单元,所以都指向新的地址
第二组输出:
浅拷贝中的引用元素仍然指向原来的子对象,所以ls2[0]和ls[0]、ls1[0]的指向的地址相同
深拷贝,由于a[0]引用指向可变对象,进一步复制了可变对象,所以ls3[0]和ls2[0]的指向不同的地址
第三组输出:
深拷贝,由于a[1]引用指向不可变对象,不会复制不可变对象,所以ls3[1]和ls2[1]指向相同的地址
第四组输出:
深拷贝,a[0][0]指向不可变对象整数型数值1;内存中只存在一个整数型数值1对象,所以任何变量指向的地址都相同

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值