天天看点

Python变量赋值时的引用传递问题变量赋值时的引用传递可变对象和不可变对象Python内存分配

  • 变量赋值时的引用传递
  • 可变对象和不可变对象
  • Python内存分配

变量赋值时的引用传递

  在Python语言中,对象是通过引用传递的。在赋值时,不管这个对象是新创建的,还是一个已经存在的,都是将该对象的引用(并不是值)赋值给变量。

  要保持追踪内存中的对象,Python使用了引用计数这一简单技术。也就是说Python内部记录着所有使用中的对象各有多少引用。一个内部跟踪变量,称为一个引用计数器。每个对象各有多少个引用,简称引用计数。当对象被创建时,就创建了一个引用计数,当这个对象不再需要时,也就是说,这个对象的引用计数变为0时,它被垃圾回收。

  当对象被创建并(将其引用)赋值给变量时,该对象的引用计数就被设置为1。当同一个对象(的引用)又被赋值给其他变量时,或作为参数传递给函数、方法或类实例时,或者被赋值为一个窗口对象的成员时,该对象的一个新的引用,或者称作别名,就被创建(则该对象的引用计数自动加1)。

  当对象的引用被销毁时,引用计数会减少。最明显的例子就是当引用离开其作用范围时,这种情况最经常出现在函数运行结束时,所有局部变量都被自动销毁,对象的引用计数也就随之减少。当变量被赋值给另外一个对象时,原对象的引用计数也会自动减1。其他造成对象的引用计数减少的方式包括使用

del

语句删除一个变量,或者当一个对象被移出一个窗口对象时(或该容器对象本身的引用计数变成了0时)。

>>> a = 
>>> b = a
>>> id(a), id(b)
(, )
>>> a = 
>>> a, b
(, )
>>> id(a), id(b)
(, )
           

  在上面的例子中,

a = 1

使变量

a

指向了整型对象

1

b = a

使变量

b

也指向了整型对象

1

,通过语句

id(a), id(b)

可以看到变量

a

和变量

b

所指向的内存地址相同。

  语句

a = 2

将新的对象

2

赋值给了变量

a

,此时变量

a

指向了对象

2

,变量

b

仍然指向原来的对象

1

。通过语句

id(a), id(b)

可以看到变量

a

与变量

b

所指向的内存地址不同。

可变对象和不可变对象

  Python语言中的对象分为可变对象和不可变对象。

  • 不可变对象,该对象所指向的内存中的值不能被改变。当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址。
  • 可变对象,该对象所指向的内存中的值可以被改变。变量(准确的说是引用)改变后,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟出新的地址,通俗点说就是原地改变。

  数字、字符串和元组是不可变类型,列表和字典是可变类型。

>>> a = 
>>> id(a)

>>> a = a + 
>>> a

>>> id(a)

           

  在上面的例子中,语句

a = a + 1

改变变量

a

的值时,由于变量

a

所指向的对象

1

是不可变对象,因而创建了新的对象

2

并使变量

a

指向了对象

2

>>> a = (, , )
>>> id(a)

>>> a = a + (, , )
>>> a
(, , , , , )
>>> id(a)

           

  可以看到使用元组的结果与整型对象相同。

>>> a = [, , ]
>>> b = a
>>> id(a), id(b)
(, )
>>> b += [, , ]
>>> a, b
([, , , , , ], [, , , , , ])
>>> id(a), id(b)
(, )
           

  上面是可变对象的一个例子,变量

a

和变量

b

同时指向了列表对象

[1, 2, 3]

,当语句

b += [4, 5, 6]

改变变量

b

的值时,并没有创建新的列表对象,而是直接改变了原先变量

a

和变量

b

所指向的列表对象。

  在Python中

a += b

a = a + b

是有区别的。

a += b

直接对变量

a

进行操作,会改变变量

a

的值。

a = a + b

中,

a + b

会生成一个新的对象,变量

a

将指向这个新对象。

>>> a = [, , ]
>>> b = a
>>> id(a), id(b)
(, )
>>> b = b + [, , ]
>>> a, b
([, , ], [, , , , , ])
>>> id(a), id(b)
(, )
           

Python内存分配

  Python中会为每个出现的对象分配内存,哪怕他们的值完全相等(注意是相等不是相同)。如执行

a = 2.0

b = 2.0

这两个语句时会先后为

2.0

这个

Float

类型对象分配内存,然后将

a

b

分别指向这两个对象。所以

a

b

指向的不是同一对象:

>>> a = 
>>> b = 
>>> a is b
False
>>> a == b
True
           

  但是为了提高内存利用效率对于一些简单的对象,如一些数值较小的

int

对象,Python采取重用对象内存的办法,如执行

a = 2

b = 2

时,由于

2

作为简单的

int

类型且数值小,Python不会两次为其分配内存,而是只分配一次,然后将

a

b

同时指向已分配的对象:

>>> a = 
>>> b = 
>>> a is b
True
           

  如果赋值的不是

2

而是大的数值,情况就跟前面的一样了:

>>> a = 
>>> b = 
>>> a is b
False
>>> id(a)

>>> id(b)

           

  两个对象即便值相等,但仍然是不同的对象。

>>> id()

>>> id()

           

参考文献

[1] Python核心编程(第二版)

[2] https://www.cnblogs.com/sun-haiyu/p/7096918.html

[3] https://blog.csdn.net/weixin_36250487/article/details/79620874