python3 赋值与内存空间

先看一个例子:

x = [1, 2, 3]
y = x
x[1] = 100

print(f"{x=} {y=}")

输出:

x=[1, 100, 3] y=[1, 100, 3]

改变变量x的值,变量y的值也随着改变,这与Python内部的赋值机制有关。

 

简单类型

先来看这一段代码在Python中的执行过程。

x = 500
y = x
y = 'foo'

print(f"{x=} {y=}")

输出:

x=500 y='foo'

Python分配了一个 PyInt 大小的内存 pos1 用来储存对象 500 ,然后,Python在命名空间中让变量 x 指向了这一块内存,注意,整数是不可变类型,所以这块内存的内容是不可变的。

执行 x = 500

内存命名空间
pos1 : PyInt(500) (不可变)x : pos1

执行 y = x

Python并没有使用新的内存来储存变量 y 的值,而是在命名空间中,让变量 y 与变量 x 指向了同一块内存空间。

内存命名空间
pos1 : PyInt(500) (不可变)x : pos1
y : pos1

执行 y = 'foo'

Python此时分配一个 PyStr 大小的内存 pos2 来储存对象 foo ,然后改变变量 y 所指的对象。

内存命名空间
pos1 : PyInt(500) (不可变)
pos2 : PyStr('foo') (不可变)
x : pos1
y : pos2

对这一过程进行验证,可以使用 id 函数。

x = 500
id(x)
# 输出 2389590724080

y = x
id(y)
# 输出 2389590724080

x is y
# 输出 True

y = 'foo'
id(y)
# 输出 2389590458928

Python会为每个出现的对象进行赋值,哪怕它们的值是一样的,例如:

x = 500
id(x)
# 输出 2389590723696

y = 500
id(y)
# 输出 2389590722000

x is y
# 输出 False

不过,为了提高内存利用效率,对于一些简单的对象,如一些数值较小的int对象,Python采用了重用对象内存的办法:

x = 2
id(x)
# 输出 140706675988160

y = 2
id(y)
# 输出 140706675988160

x is y
# 输出 True

 

容器类型

现在来看另一段代码:

x = [500, 501, 502]
y = x
y[1] = 600
y = [700, 800]

print(f"{x=} {y=}")

输出:

x=[500, 600, 502] y=[700, 800]

执行 x = [500, 501, 502]

Python为3个PyInt分配内存 pos1 , pos2 , pos3 (不可变),然后为列表分配一段内存 pos4 ,它包含3个位置,分别指向这3个内存,最后再让变量 x 指向这个列表。

内存命名空间
pos1 : PyInt(500) (不可变) 
pos2 : PyInt(501) (不可变) 
pos3 : PyInt(502) (不可变) 
pos4 : PyList(pos1, pos2, pos3) (可变)
x : pos4

执行 y = x

并没有创建新的对象,只需要将 y 指向 pos4 即可。

内存命名空间
pos1 : PyInt(500) (不可变) 
pos2 : PyInt(501) (不可变) 
pos3 : PyInt(502) (不可变) 
pos4 : PyList(pos1, pos2, pos3) (可变)
x : pos4
y : pos4

执行 y[1] = 600

原来 y[1] 这个位置指向的是 pos2 ,由于不能修改 pos2 的值,所以首先为 600 分配新内存 pos5 。

再把 y[1] 指向的位置修改为 pos5 。此时,由于 pos2 位置的对象已经没有用了,Python会自动调用垃圾处理机制将它回收。

内存命名空间
pos1 : PyInt(500) (不可变) 
pos2 : 垃圾回收 
pos3 : PyInt(502) (不可变) 
pos4 : PyList(pos1, pos5, pos3) (可变)
pos5 : PyInt(600) (不可变)
x : pos4
y : pos4

执行 y = [700, 800]

首先创建这个列表,然后将变量 y 指向它。

内存命名空间
pos1 : PyInt(500) (不可变) 
pos3 : PyInt(502) (不可变) 
pos4 : PyList(pos1, pos5, pos3) (可变)
pos5 : PyInt(600) (不可变) 
pos6 : PyInt(700) (不可变)
pos7 : PyInt(800) (不可变)
pos8 : PyList(pos6, pos7) (可变)
x : pos4 
y : pos8

 

过程验证

执行代码

x = [500, 501, 502]
print(id(x[0]))
print(id(x[1]))
print(id(x[2]))
print(id(x))

输出:

48220224
48220248
48220200
54993032

执行代码

y = x
print(id(y))

输出

54993032

执行代码

x is y

输出

True

执行代码

y[1] = 600
print(id(y))

输出

54993032

执行代码

print(id(x[1]))
print(id(y[1]))

输出

48220272
48220272

执行代码

print(id(y))
print(id(x))

输出

54995272
54993032

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值