详解Python变量在内存中的存储

文章面向Python初学者,结合数据与内存形态讲解Python数据。介绍了引用与对象的关系,对象值存于内存,外部通过引用使用;阐述可变与不可变数据类型的区别;说明了引用传递和值传递的特点;还讲解了赋值拷贝、浅拷贝和深拷贝的不同。

这篇文章主要是对python中的数据进行认识,对于很多初学者来讲,其实数据的认识是最重要的,也是最容易出错的。本文结合数据与内存形态讲解python中的数据,内容包括:

  • 引用与对象
  • 可变数据类型与不可变数据类型
  • 引用传递与值传递
  • 深拷贝与浅拷贝

id函数:你可以通过python的内置函数 id() 来查看对象的身份(identity),这个所谓的身份其实就是 对象 的内存地址

一、引用与对象:引用与对象的关系: 
这里写图片描述

#创建两个对象
name1='wupeiqi'
name2='alex'
  • 1
  • 2
  • 3

这里写图片描述

对象:当创建数据对象时,在内存中会保存对象的值,这个值就是对象自己;(字符串对象:”wupeiqi”) 
引用:对象保存在内存空间,外部想要使用对象的值,需要使用引用,就是‘name1’,’name2’。内存会保存对象的引用数量,当某个对象的引用数量为0时,对象会被回收。

二、可变数据类型与不可变数据类型 
1,数据分类:

  • 可变数据类型:列表list和字典dict
  • 不可变数据类型:整型int、浮点型float、字符串型string和元组tuple

这里的可变不可变,是指内存中的那块内容(value)是否可以被改变。如果是不可变类型,在对对象本身操作的时候,必须在内存中新申请一块区域(因为老区域不可变)。如果是可变类型,对对象操作的时候,不需要再在其他地方申请内存,只需要在此对象后面连续申请(+/-)即可,也就是它的address会保持不变,但区域会变长或者变短。

(1)python中的不可变数据类型,不允许变量的值发生变化,如果改变了变量的值,相当于是新建了一个对象,而对于相同的值的对象,在内存中则只有一个对象,内部会有一个引用计数来记录有多少个变量引用这个对象; 
(2)可变数据类型,允许变量的值发生变化,即如果对变量进行append、+=等这种操作后,只是改变了变量的值,而不会新建一个对象,变量引用的对象的地址也不会变化,不过对于相同的值的不同对象,在内存中则会存在不同的对象,即每个对象都有自己的地址,相当于内存中对于同值的对象保存了多份,这里不存在引用计数,是实实在在的对象。”

2,不可变数据类型:不可变是指对象本身的值是不可变的(当你创建a=1整型对象,用a去引用它,内存中的对象1是不变得,当执行a=2时,只是重新创建了对象2,用a引用,如果1对象没有其他引用会被回收)

>>> x = 1  
>>> id(x)  
31106520  
>>> y = 1  
>>> id(y)  
31106520  
>>> x = 2  
>>> id(x)  
31106508  
>>> y = 2  
>>> id(y)  
31106508  
>>> z = y  
>>> id(z)  
31106508  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

解释:这里的不可变大家可以理解为x引用的地址处的值是不能被改变的,也就是31106520地址处的值在没被垃圾回收之前一直都是1,不能改变,如果要把x赋值为2,那么只能将x引用的地址从31106520变为31106508,相当于x = 2这个赋值又创建了一个对象,即2这个对象,然后x、y、z都引用了这个对象,所以int这个数据类型是不可变的,如果想对int类型的变量再次赋值,在内存中相当于又创建了一个新的对象,而不再是之前的对象。从下图中就可以看到上面程序的过程。 
这里写图片描述

3,可变对象:可变是指对象本身的值是可变的list,dict对象的值其实是引用了其他对象,当改变对象的值时,其实是引用了不同的对象)

>>> a = [1, 2, 3]  
>>> id(a)  
41568816  
>>> a = [1, 2, 3]  
>>> id(a)  
41575088  
>>> a.append(4)  
>>> id(a)  
41575088  
>>> a += [2]  
>>> id(a)  
41575088  
>>> a  
[1, 2, 3, 4, 2]  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

解释:(1)进行两次a = [1, 2, 3]操作,两次a引用的地址值是不同的,也就是说其实创建了两个不同的对象,这一点明显不同于不可变数据类型,所以对于可变数据类型来说,具有同样值的对象是不同的对象,即在内存中保存了多个同样值的对象,地址值不同。 
(2)我们对列表进行添加操作,分别a.append(4)和a += [2],发现这两个操作使得a引用的对象值变成了上面的最终结果,但是a引用的地址依旧是41575088,也就是说对a进行的操作不会改变a引用的地址值,只是在地址后面又扩充了新的地址,改变了地址里面存放的值,所以可变数据类型的意思就是说对一个变量进行操作时,其值是可变的,值的变化并不会引起新建对象,即地址是不会变的,只是地址中的内容变化了或者地址得到了扩充。下图对这一过程进行了图示,可以很清晰地看到这一过程。 
这里写图片描述

三、引用传递与值传递:可变对象为引用传递,不可变对象为值传递。(函数传值) 
1,引用传递:当传递列表或者字典时,如果改变引用的值,就修改了原始的对象。

# 添加了一个string类型的元素添加到末尾

def ChangeList(lis):
    lis.append('hello i am the addone')
    print lis
    return

lis = [1, 2, 3]
ChangeList(lis)
print lis

输出:
[1,2,3, 'hello i am the addone']

[1,2, 3,'hello i am the addone']
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

2,值传递:当传递不可变对象时,如果改变引用的值,只是创建了不同的对象,原始对象并没有改变。

def ChangeString(string):
    string = 'i changed as this'
    print string
    return

string = 'hello world'
ChangeString(string)
print string

输出:
i changed as this

hello world
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

四、深拷贝与浅拷贝: 
copy.copy() 浅拷贝;copy.deepcopy() 深拷贝。浅拷贝是新创建了一个跟原对象一样的类型,但是其内容是对原对象元素的引用。这个拷贝的对象本身是新的,但内容不是拷贝序列类型对象(列表\元组)时,默认是浅拷贝。

1,赋值拷贝: 
赋值,只是创建一个变量,该变量指向原来内存地址:n4 = n3 = n2 = n1 = “123/’Wu’” 
这里写图片描述

2,浅拷贝:在内存中只额外创建第一层数据

import copy
n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 456]}
n3 = copy.copy(n1)
  • 1
  • 2
  • 3

这里写图片描述

import copy
a = [1,[[2,3],5],3]
b = a.copy()    #copy.copy(a)

print(id(a[1]))
print(id(b[1]))

c = copy.deepcopy(a)
print(id(c[1]))

输出:
3021497843400
3021497843400
3021497854728
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

3,深拷贝:在内存中将所有的数据重新创建一份(排除最后一层,即:python内部对字符串和数字的优化)

import copy
n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 456]}
n4 = copy.deepcopy(n1)
  • 1
  • 2
  • 3

这里写图片描述

参考文献: 
http://blog.youkuaiyun.com/dan15188387481/article/details/49864613 
https://www.cnblogs.com/lfpython/p/7207747.html 
https://www.cnblogs.com/huamingao/p/5809936.html 
https://www.cnblogs.com/jiangzhaowei/p/5740913.html

<think>我们正在讨论Python变量内存中的存储机制。根据用户的问题,他想要理解Python变量是如何在内存存储的,包括存储方式、原理等。 根据提供的引用,我们可以总结以下几点: 1. Python内存管理包括栈区和堆区(引用[1])。 2. Python变量存储原理中,变量名与内存地址的关联,以及当变量重新赋值时,会断开旧链接,指向新内存地址(引用[3])。 3. 深拷贝会复制所有数据,生成一套完全一样的内容,修改会影响原变量引用[2])。 因此,我们可以这样组织回答: 首先,介绍Python变量的创建和赋值过程,变量名与内存地址的绑定关系。 其次,解释可变类型和可变类型在内存中的区别,以及赋值、浅拷贝和深拷贝的同。 然后,可以结合引用中的例子,变量重新赋值时内存的变化。 最后,简要介绍Python的垃圾回收机制如何管理内存。 注意:回答中要使用中文,并按照系统级指令的要求使用$...$和$$...$$格式来书写数学表达式(虽然本问题中可能较少用到数学表达式,但如果有变量名等,可以用行内数学格式)。 另外,根据用户要求,生成相关问题。 下面开始回答:</think>### Python变量内存存储机制详解Python中,变量内存存储机制基于**对象引用模型**,核心原理如下: #### 1. **变量名与内存地址的绑定** - 变量名实质上是内存对象的**标签(引用)** - 赋值操作 `name = 'Kwan'` 的底层过程: 1. 在堆内存创建字符串对象 `'Kwan'` 2. 在栈区创建变量名 `name` 3. 建立 `name` 到对象内存地址的引用关系 - 重新赋值 `name = 'Kobe'` 时: 1. 新建 `'Kobe'` 对象 2. 将 `name` 的引用指向新地址 3. 原 `'Kwan'` 对象成为垃圾(若无其他引用)[^3] ```mermaid graph LR A[变量名name] -->|初始指向| B[内存地址1:'Kwan'] A -->|重新赋值| C[内存地址2:'Kobe'] ``` #### 2. **可变 vs 可变对象** - **可变对象**(数字、字符串、元组): - 创建后内存内容可修改 - 重新赋值实质是创建新对象 - 示例: `a = 5; b = 5` → `id(a) == id(b)` # 相同内存地址 - **可变对象**(列表、字典、集合): - 内存内容可修改 - 变量引用地址变 - 示例: ```python lst1 = [1,2] # 内存地址0x1000 lst1.append(3) # 仍为0x1000 ``` #### 3. **内存管理机制** - **引用计数**: - 每个对象维护计数器统计引用数量 - 归零时触发垃圾回收 ```python import sys a = [1,2,3] sys.getrefcount(a) # 返回引用计数 ``` - **分代回收**: - 根据对象存活时间分为三代 - 优先回收新生代对象[^1] #### 4. **拷贝操作的差异** | 操作类型 | 特点 | 内存影响 | |---------|------|---------| | **赋值** | 创建新引用 | 共享同一内存对象 | | **浅拷贝** | `copy.copy()` | 只复制顶层对象 | | **深拷贝** | `copy.deepcopy()` | 递归复制所有子对象[^2] | **示例对比**: ```python import copy origin = [1, [2,3]] # 浅拷贝 shallow = copy.copy(origin) shallow[1][0] = 99 # 修改影响origin # 深拷贝 deep = copy.deepcopy(origin) deep[1][0] = 100 # 影响origin ``` #### 5. **内存优化特性** - **小整数池**:[-5,256] 整数预分配 - **字符串驻留**:相同字符串共享内存 - **空元组复用**:所有空元组指向同一对象 ### 内存结构示意图 ``` 栈区(Stack) 堆区(Heap) +---------------+ +-----------------+ | 变量名 | 引用 | 对象 | +---------------+ +-----------------+ | name ───────┼─────────→ | 'Kobe' (str) | | | +-----------------+ | lst ───────┼─────────→ | [1,2,3] (list) | +---------------+ +-----------------+ ```
评论 7
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值