Python变量内存地址

发布于 2021 年 07 月 29 日 | 更新于 2021 年 08 月 17 日

变量的实现方式

不可变&可变数据类型

不可变数据类型 Immutable

intfloatstrtuple

不允许内存中的值发生变化,要修改值(运算)的情况,不会将修改后的值放回(覆盖)原地址,而是直接新增一块地址空间,来存放修改后的值,然后使变量(引用)指向新的地址空间

除tuple外,不可变数据在整个内存中是唯一的,相同值只能存在一个地址中

可变数据类型 Mutable

listdictclass

我理解成容器类型(二级指针?指向数组的指针?),允许容器内的值发生变化(append+=),而容器本身的地址不会变化,容器内的元素也可以是容器

在整个内存中,相同值可以存在多份(多个不同地址空间),由此想到以下两种情况:

  1. 相同的容器(立即数?)赋值给多个变量(不是赋值拷贝):这些变量的id不一样

  2. 多次赋值给同一变量(不是赋值拷贝):变量的id会发生变化(那上一个地址空间被销毁了吗?)

>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> id(a)
140508626205888
>>> id(b)
140508626146176
>>> a = [1, 2, 3]  # 上一个地址(...5888)就被回收了,下次赋值时使用
>>> id(a)
140508626145984
>>> c = [1, 2, 3]  # 使用了上面被回收的地址(...5888)
>>> id(c)
140508626205888

'''
感觉tuple像介于可变和不可变之间的东西
即满足容器的一些特性
又不能修改容器的内容
'''
>>> a = (1, 2, 3)
>>> b = (1, 2, 3)
>>> id(a)
140274137290688
>>> id(b)
140274137163712
>>> a += (4, 5)
>>> id(a)
140274137546560

底层实现的猜想:

  1. 每当给变量赋值一个容器(立即数?)时,都会在一个新的地址空间中生成容器,然后让变量指向(引用)该容器
  2. 如果赋给同一变量,那么之前的地址空间就被回收,供下次使用

ref

浅拷贝和深拷贝

3种拷贝方式:ref

赋值拷贝(=)

只是新建一个引用,两个变量(引用)指向同一个地址

如果把引用比作快捷方式,那么=就相当于新增了一个快捷方式,指向相同的地址

函数传参相当于形参=实参

=_from_runoob

浅拷贝(copy)

只拷贝父对象,不拷贝父对象内部的子对象,只拷贝子对象的引用

只拷贝第一层变量(最外层容器本身),内部变量相当于赋值拷贝(快捷方式)

copy_from_runoob

深拷贝(deepcopy)

完全拷贝所有内容,开辟全新的内存空间

deepcopy_from_runoob

import copy
a = [1, [2, 3]]
a_ = a
a_copy = a.copy()  # <==> a_copy = copy.copy(a)
a_deepcopy = copy.deepcopy(a)
print(id(a))             # 139751405433920
print(id(a[0]))          # 93898578046432
print(id(a[1]))          # 139751404912448
print(id(a_))            # 139751405433920
print(id(a_[0]))         # 93898578046432
print(id(a_[1]))         # 139751404912448
print(id(a_copy))        # 139751406233856
print(id(a_copy[0]))     # 93898578046432
print(id(a_copy[1]))     # 139751404912448
print(id(a_deepcopy))    # 139751405539072
print(id(a_deepcopy[0])) # 93898578046432
print(id(a_deepcopy[1])) # 139751406590080

Hash

__hash__()方法的对象称为Hashable

不可变对象一定是可哈希的Hashable,Hashable不一定是不可变对象

hash(object)返回object.__hash__()方法的值(根据机器字长进行截断)

class A:
    def __hash__(self):
        return 1

a = A()
hash(a)  # 1

Python Hashes and Equality

What does “hashable” mean in Python?

Python中的__hash__和__eq__方法之间的一些使用问题

总结

  1. Python中所有变量都是引用(指针)
  2. 不可变数据发生变化时,生成新值,而不是修改原值,相同的值在内存中是唯一
  3. 可变数据变化时,不会重新分配新的容器地址,只改变容器内的变量
  4. 三种拷贝方式:对于不可变数据,不管哪种拷贝,都只是快捷方式,id都相同。只有操作可变数据,才能体现出差异性