【一】深浅拷贝
【1】深浅拷贝问题
- 无论深拷贝还是浅拷贝都是用来复制对象的
(1)浅拷贝
- 浅拷贝,只会复制一层,如果 copy 的对象中有可变数据类型,修改可变数据类型还是会影响拷贝的对象
# 【1】浅拷贝
# 必须是修改源数据类型中的可变数据类型才生效
# num_list = [1, 2, 3, 4, [1, 2]]
# num_list_new = copy.copy(num_list)
# num_list_new[4].append(5)
# print(num_list, id(num_list))
# print(num_list_new, id(num_list_new))
# [1, 2, 3, 4, [1, 2, 5]] 2504131502208
# [1, 2, 3, 4, [1, 2, 5]] 2504131490752
(2)深拷贝
- 深拷贝是完整复制,无论可变或不可变,都是创建出新的来,都是创建出新的来,以后再改原对象,都不会对 copy 出的对象造成影响
# 【2】深拷贝
# 深拷贝会将原来的列表完全复制一份,修改新列表不会影响到原来的列表
# num_list = [1, 2, 3, 4, [1, 2]]
# num_list_new = copy.deepcopy(num_list)
# num_list_new[4].append(5)
#
# print(num_list, id(num_list))
# print(num_list_new, id(num_list_new))
#[1, 2, 3, 4, [1, 2]] 2672975779456
#[1, 2, 3, 4, [1, 2, 5]] 2672975779712
【总结】
- 浅拷贝只会复制顶层对象,而不会影响到深层的可变数据类型
- 在复制出来的列表,列表中的列表引用的是原来列表的引用
- 深拷贝会递归的复制整个对象的数据结构
- 在复制出来的列表,列表中的列表引用的是新列表的引用
【二】垃圾回收机制(GC机制)
【1】什么是垃圾回收机制
- 垃圾回收机制(简称GC)是Python解释器自带的一种机制
- 专门用来回收不可用的变量值所占用的内存空间(在内存中,没有变量名指向的数据都是垃圾数据)
【2】为什么要有垃圾回收机制
- 程序运行过程中会申请大量的内存空间,而对于一些无用的内存空间如果不及时清理的话会导致内存使用殆尽(内存溢出),导致程序崩溃
- 因此管理内存是一件重要且繁杂的事情,而 python 解释器自带的垃圾回收机制把程序员从繁杂的内存管理中解放出来
【3】GC原理
【一】堆区和栈区
- 在定义变量时,变量名与变量值都是需要存储的
- 分别对应内存中的两块区域:
- 堆区
- 变量名与值内存地址的关联关系存放于栈区
- 栈区
- 变量值存放于堆区,内存管理回收的则是堆区的内容
- 堆区
#记住三个名字
# 引用计数为主,标记清除为辅,分代回收
#【1】引用计数
# 变量值被变量名指向的次数
x = 10# 10被 x 指向1次
y = x # y 指向 x ,x 指向 y ,以上2次
#【2】标记清除
#当一个变量值被引用的时候,Python自带的垃圾回收机制会定期扫描
#如果这个变量值有引用 ---> 不管
#如果这个变量值没有用 ---> 标记
#【3】分代回收
#新生代 ---> 第一次被扫描,扔到新生代中
#青春代 ---> 知道达到新生代扫描阈值,如果还没人指向直接挪到 青春代
#老年代 ---> 知道达到青春代扫描阈值,如果还没人指向直接挪到 老年代
# ---> 知道达到老年代扫描阈值,如果还没人指向直接清除 del
【3】堆区
- 变量值存放于堆区,内存管理回收的则是堆区的内容
- 在Python中,变量和它们所引用的对象(如数值、字符串、列表、字典等)的存储位置与内存管理机制密切相关。其中提到的 “变量值存放于堆区” 主要涉及两个概念:变量本身和它所引用的对象。
(1)变量
- 变量是程序中用于标识数据的一个符号或名字,它并不直接存储数据,而是指向(或引用)实际存储数据的内存地址。
- 在Python解释器内部,变量名通常保存在栈(Stack)中。栈是一种后进先出(LIFO)的数据结构,用于高效地管理函数调用时的局部变量、返回地址等信息。
- 创建一个变量时,Python会在当前作用域(如全局作用域或函数局部作用域)的栈空间内为该变量分配一个名称,并将其关联到相应的对象。
(2)对象及值
- Python中的对象(即变量所引用的实际数据)通常存储在堆(Heap)中。
- 堆是一种动态分配内存的区域,相比于栈,其大小更为灵活且无固定上限。
- 堆用于存储需要大量、不固定大小内存空间的数据结构,如大数组、复杂的数据类型(如类实例)、以及那些生命周期可能跨越多个函数调用或作用域的对象。
【4】栈区
- 变量名与值内存地址的关联关系通常被存放于程序的栈区
(1)栈区的特性与作用
-
栈(Stack)是计算机内存中的一种数据结构,其主要特点是后进先出(LIFO)。在编程语言执行过程中,栈区用于存储局部变量、函数参数以及函数调用时的返回地址等临时性信息。
-
栈区的操作快速且高效,因为它遵循简单的压栈(push)和弹栈(pop)规则,适合处理那些生命周期短、使用频繁且需要保持特定顺序的数据。
(2)变量生命周期管理
- 变量名与值内存地址的关联关系本质上是一种符号表信息,它记录了源代码中定义的变量名与其所对应的内存地址之间的映射。
- 这种信息具有明显的生命周期特征:当进入一个作用域(如函数或代码块)时,新声明的变量及其关联关系应被创建。
【三】编解码(二进制)
# 【1】解码 ---> 将二进制数据转换为字符
# data = b'\xe8\x9a\xa9\xe6\xa2\xa6'
# print(data.decode('utf-8')) #蚩梦
# print(data.decode('gbk')) #铓╂ⅵ
# print(data.decode('shift-jis')) #陏ゥ譴ヲ
#
# name = b'dream'
# print(type(name)) #<class 'bytes'>
# # 【2】编码 ---> 将字符转换为二进制数据
# name = '蚩梦'
# # 指定编码格式 ---> 转换成那种语言的二进制四数据
# print(name.encode('utf-8')) #b'\xe8\x9a\xa9\xe6\xa2\xa6'
【四】文件操作
【1】打开文件的两种方式
【1】使用open语句
- w 覆盖写
- a 追加写
- r 读
- 参数是 (文件路径,打开文件的模式,编码格式)
# (1)打开文件并付给句柄对象
# fp = open('01.text', 'r', encoding='utf-8')
# print(fp) # <_io.TextIOWrapper name='01.text' mode='r' encoding='utf-8'>
# (2) 读取所有文件数据
# data = fp.read()
# (3)打印
# print(data)
# fp.close()
【2】with 语句
- 在with语句内部打开文件凑在哦以后自动 执行 close() 把文件关闭、 --- 节省资源
# with open('01.text','r',encoding='utf-8') as fp:
# data = fp.read()
# print(data)
【2】文件的操作模式
【一】读模式 r
# with open('01.text', 'r', encoding='utf8') as fp:
# data = fp.read()
# username = data.split('|')
# print(username)
【二】覆盖写模式 w
# 覆盖写 :
# 文件名存在则打开,不存在则新建
# 打开当前文件并且将文件内容清空然后写入新的内容
# (1)一次性写入
# data = 'opp|366'
# with open(file='01.text', mode='w', encoding='utf8') as fp:
# # 写的内容
# fp.write(data)
# (2)连续写入
# data = 'opp|366'
# with open(file='01.text', mode='w', encoding='utf8') as fp:
# # 写的内容
# # 只要保证当前文件是打开的前提下就可以一直写内容
# fp.write(data)
# fp.write("哈哈哈")
# fp = open('01.text', 'w', encoding='utf-8')
# fp.write('aaa')
# fp.write('bbb')
# fp.write('ccc')
# fp.write('ddd')
# fp.close()
# # I/O operation on closed file. 文件已经关闭无法再继续操作
# fp.write('9999')
【三】追加写模式 a
# 文件不存在则新建并写入
# 文件存在则打开并继续写入
# 不自带换行功能
# dream|666dream|666\ndream|666\n
# data = 'dream|666'
# with open('01.text', 'a', encoding='utf8') as fp:
# fp.write(data + '\n')
文件操作扩展模式
# r+ / w+ /a+
# r :只读
# w:只写无法读
# with open('01.text', 'r+', encoding='utf-8') as fp:
# fp.write('666')
# # 写完以后需要进行持久化保存才能被读出来,否则是空的
# data = fp.read()
# print(data)
【3】文件操作方法
- 数据准备
# data = 'dream|521'
# with open('01.text', 'a', encoding='utf8') as fp:
# fp.write(data + '\n')
- r模式的方法详细
# with open('01.text', 'r', encoding='utf8') as fp:
# (1)一次性全部读完
# data = fp.read()
# (2)每次只读一行
# count = 0
# while True:
# count += 1
# data = fp.readline()
# if data:
# print(count, data)
# else:
# break
# (3)一次性读很多行 ---> 把所有数据读出来后放到一个列表中
# data = fp.readlines()
# print(data)
# (4)测试当前对象是否可读
# print(fp.readable())
# fp = open('01.text', 'r', encoding='utf8')
# fp.close()
# print(fp.readable())
- 写模式的操作方法
# data_list = ['111', '\n', '222','\n']
# with open('01.text', 'a', encoding='utf-8') as fp:
# # (1)将原本内容清空并一次性写入新内容
# # fp.write('666')
# # (2)逐个元素进行写入
# fp.writelines(data_list)
# with open('01.text', 'w+', encoding='utf-8') as fp:
# fp.write('999')
# # 将文件写完后立马刷新到硬盘中
# fp.flush()
# data = fp.read()
# print(data)
【4】控制文件指针
# read 方法补充
# with open('01.text', 'r', encoding='utf8') as fp:
# # read 可以放参数,参数是读取到哪个索引位置
# data = fp.read(4)
# print(data)
seek 函数
# f.seek(指针移动的字节数,模式控制):
# 0: 默认的模式,该模式代表指针移动的字节数是以文件开头为参照的
# 1: 该模式代表指针移动的字节数是以当前所在的位置为参照的
# 2: 该模式代表指针移动的字节数是以文件末尾的位置为参照的
# with open('girl.jpg', 'rb') as fp:
# 以开头作为参照向后移动1个字符
# 英文字符是占了一个字符的位置 中文字符是占三个字符的位置
# fp.seek(3, 0)
# 查看当前指针所在的索引位置
# print(fp.tell()) # 3
# 0 : 以开头作为参照用一次重置一次
# fp.seek(3, 0)
# print(fp.tell()) # 3
# (2)1 模式: 以当前位置作为参照向后移动
# 在Python3.x版本之后不允许在文本文件中使用
# 只能在二进制中使用
# print(fp.read()) # 真是帅爆啦!
# fp.seek(3, 1)
# (2)2模式:以结尾位置作为参照移动
# fp.seek(-2, 2) # \x82'
# print(fp.tell())
# print(fp.read())
标签:栈区,fp,open,堆区,list,num,print,拷贝,data
From: https://www.cnblogs.com/chosen-yn/p/18122508