生成器创建方式、浅拷贝与深拷贝区别与案例比较

一、生成器

生成器:能够根据程序员指定的规则循环生成数据,条件不成立时生成数据结束,数据不是一次性全部生成处理,而是用一个再生成一个,是根据算法生成数据的一种机制,每次调用生成器只生成一个值,可以节省大量内存

创建生成器方式

  • 生成器推导式
  • yield关键字

1.1 生成器推导式

类似列表推导式,生成器推导式使用小括号

  • next()函数:用来获取生成器中下一个值,从第一个开始获取
  • for循环:遍历生成器中每一个元素值,若其前已用next()获取,则从next()获取的元素后开始获取
generator = (i**2 for i in range(6))  # 创建生成器
print(generator)
print('生成器中下一个值为:', next(generator), '再下一个值为:', next(generator))
for v in generator:  # 用一个生成一个,从next()之后继续生成
    print('遍历生成器,剩余值依次为:', v)

输出:
<generator object <genexpr> at 0x000001BB6E8D2FF0>
生成器中下一个值为: 0 再下一个值为: 1
遍历生成器,剩余值依次为: 4
遍历生成器,剩余值依次为: 9
遍历生成器,剩余值依次为: 16
遍历生成器,剩余值依次为: 25

1.2 yield关键字

只要在def定义的函数中看到yield关键字,则该函数即为一个生成器

  • 代码执行到yield时会暂停,然后将结果返回出去,下次启动生成器会在暂停的位置继续往下执行
  • 生成器如果把数据生成完成,再次获取生成器中的下一个数据会抛出一个StopIteration 异常,表示停止迭代异常
  • while 循环内部没有处理异常操作,需手动添加处理异常操作
  • for 循环内部自动处理了停止迭代异常,使用更方便,推荐使用
def generator():   # 函数中有yield关键字,则该函数即为生成器
    for i in range(6):
        print('开始生成数据')
        yield i + 2  # 执行到此处时,程序暂停执行,并将结果返回,当再次启动生成器时继续从此处往下执行
        print('完成一次数据生成')
result = generator()
print('result类型为:', type(result))
print('生成器中下一个值为:', next(result))
print('--------------------------------')
print('生成器中再下一个值为:', next(result))

# 遍历生成器方式1,for循环
# for i in result:   # for循环自动处理停止迭代异常
#     print('遍历生成器,剩余值依次为:', i)

# 遍历生成器方式2,while循环
# while True:
#     try:
#         print('遍历生成器,剩余值依次为:', next(result))
#     except StopIteration as e:  # 处理迭代异常,生成最后一个数据时,会产生StopIteration异常
#         break

输出:
result类型为: <class 'generator'>
开始生成数据
生成器中下一个值为: 2
--------------------------------
完成一次数据生成
开始生成数据
生成器中再下一个值为: 3

1.3 生成器应用场景

使用生成器实现斐波那契数列,每次取值都通过算法来生成下一个数据,生成器每次调用只生成一个数据,可以节省大量的内存

斐波拉契数列(Fibonacci):数列中第一个数为0,第二个数为1,其后的每一个数都由前两个数相加得到

def fibonacci(n):   # n为生成个数
    a = 0  # 初始化前两个值
    b = 1
    i = 0  # 记录生成个数的索引
    while i < n:
        result = a
        a, b = b, a + b  # 交换变量间的值
        i += 1
        yield result  # 代码执行到yield会暂停,然后把结果返回出去,下次启动生成器会在暂停的位置继续往下执行

generator = fibonacci(20)  # 创建生成器
list = []
for v in generator:
    list.append(v)
print('生成器生成的斐波那契数列值依次为:', list)

输出:
生成器生成的斐波那契数列值依次为: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181]

二、浅拷贝与深拷贝

浅拷贝至多拷贝对象的一层,深拷贝可能拷贝多层,不论是浅拷贝或是深拷贝,只要拷贝成功就会开辟新的内存空间存储拷贝的对象

2.1浅拷贝

使用copy()函数进行浅拷贝,只对可变类型第一层对象进行拷贝,对拷贝对象开辟新的内存空间进行存储,不拷贝对象内部子对象

  • 不可变类型:数字、字符串、元组,浅拷贝不会给拷贝的对象开辟新的内存空间,而只是拷贝了这个对象的引用
  • 可变类型:列表、字典、集合,浅拷贝只对可变类型的第一层对象进行拷贝,对拷贝的对象会开辟新的内存空间进行存储,子对象不进行拷贝
import copy
print('----------------------------------------不可变类型-------------------------------')
# 拷贝后内存地址未变,说明未对对象进行拷贝,即浅拷贝不会对不可变类型的拷贝开辟新的内存空间
# 对不可变类型的浅拷贝实际是对引用(内存地址)的拷贝,两个变量指向同一个内存地址
print('-------------------数字------------------------')
a = 13
b = copy.copy(a)
print('a值为:', a, 'a的内存地址为:', id(a), '\nb值为:', b, 'b的内存地址为:', id(b))
print('-------------------字符串-----------------------')
aa = 'abc'
bb = copy.copy(aa)
print('aa值为:', aa, 'aa的内存地址为:', id(aa), '\nbb值为:', bb, 'bb的内存地址为:', id(bb))
print('--------------------元组------------------------')
a2 = (1, 3, ['a', 2])
b2 = copy.copy(a2)
print('a2值为:', a2, 'a2的内存地址为:', id(a2), '\nb2值为:', b2, 'b2的内存地址为:', id(b2))
print('a2[2]值为:', a2[2], 'a2[2]的内存地址为:', id(a2[2]), '\nb2[2]值为:', b2[2], 'b2[2]的内存地址为:', id(b2[2]))

print('\n---------------------------------可变类型-------------------------------------')
# 只对可变类型第一层对象进行拷贝,开辟新的内存空间
# 不会拷贝最外层对象内的子对象
print('--------------------列表------------------------')
a3 = [1, 3, 5]
b3 = copy.copy(a3)
print('a3值为:', a3, 'a3的内存地址为:', id(3), '\nb3值为:', b3, 'b3的内存地址为:', id(b3))
print('--------------------字典------------------------')
a4 = {'id': 2, 'name': 'xx', 'age': '18'}
b4 = copy.copy(a4)
print('a4值为:', a4, 'a4的内存地址为:', id(a4), '\nb4值为:', b4, 'b4的内存地址为:', id(b4))
print('--------------------集合------------------------')
a5 ={1, 2, 3, 'aaa'}
b5 = copy.copy(a5)
print('a5值为:', a5, 'a5的内存地址为:', id(a5), '\nb5值为:', b5, 'b5的内存地址为:', id(b5))
print('--------------------列表-----子对象--------------')
a6 = [1, 3, 5, [2, 4]]
b6 = copy.copy(a6)
print('a6值为:', a6, 'a6的内存地址为:', id(a6), '\nb6值为:', b6, 'b6的内存地址为:', id(b6))  # 地址不同,只对第一层对象进行了拷贝,开辟了新空间
print('a6[3]值为:', a6[3], 'a6[3]的内存地址为:', id(a6[3]), '\nb6[3]值为:', b6[3], 'b3[3]的内存地址为:', id(b6[3]))  # 子对象内存地址不变,说明不会拷贝子对象
a6.append(6)
print('a6追加数据6后为:', a6, 'b6为:', b6)  # 只有a6追加了,b6未追加,说明不是同一个地址
a6[3][0] = 1
print('a6子对象修改后为:', a6, ',b6为:', b6)  # a6和b6的子对象均进行了修改,说明子对象是同一个地址

输出:
----------------------------------------不可变类型-------------------------------
-------------------数字------------------------
a值为: 13 a的内存地址为: 1938320720496 
b值为: 13 b的内存地址为: 1938320720496
-------------------字符串-----------------------
aa值为: abc aa的内存地址为: 1938321990192 
bb值为: abc bb的内存地址为: 1938321990192
--------------------元组------------------------
a2值为: (1, 3, ['a', 2]) a2的内存地址为: 1938325111232 
b2值为: (1, 3, ['a', 2]) b2的内存地址为: 1938325111232
a2[2]值为: ['a', 2] a2[2]的内存地址为: 2211555234752   # 子对象内存地址相同,未开辟新空间
b2[2]值为: ['a', 2] b2[2]的内存地址为: 2211555234752

n---------------------------------可变类型-------------------------------------
--------------------列表------------------------
a3值为: [1, 3, 5] a3的内存地址为: 1938320720176 
b3值为: [1, 3, 5] b3的内存地址为: 1938604134016
--------------------字典------------------------
a4值为: {'id': 2, 'name': 'xx', 'age': '18'} a4的内存地址为: 1938322030912 
b4值为: {'id': 2, 'name': 'xx', 'age': '18'} b4的内存地址为: 1938322031168
--------------------集合------------------------
a5值为: {'aaa', 1, 2, 3} a5的内存地址为: 1938604957408 
b5值为: {'aaa', 1, 2, 3} b5的内存地址为: 1938604957856
--------------------列表-----子对象--------------
a6值为: [1, 3, 5, [2, 4]] a6的内存地址为: 1938604134528 
b6值为: [1, 3, 5, [2, 4]] b6的内存地址为: 1938604134208
a6[3]值为: [2, 4] a6[3]的内存地址为: 1938604134976    # 子对象内存地址相同,未开辟新空间
b6[3]值为: [2, 4] b3[3]的内存地址为: 1938604134976
a6追加数据6后为: [1, 3, 5, [2, 4], 6] b6为: [1, 3, 5, [2, 4]]   
a6子对象修改后为: [1, 3, 5, [1, 4], 6] ,b6为: [1, 3, 5, [1, 4]]

2.2 深拷贝

使用deepcopy()进行深拷贝,只要对象有可变类型就会对该对象到最后一个可变类型的每一层对象进行拷贝,对每一层拷贝的对象都会开辟新的内存空间进行存储

不可变类型

  • 若子对象无可变类型,则不拷贝,只对引用(内存地址)拷贝
  • 若子对象有可变类型,则对该对象到最后一个可变类型的每一层对象进行拷贝,每一层拷贝的对象都会开辟新的内存空间

可变类型

  • 会对该对象到最后一个可变类型的每一层对象进行拷贝,对每一层拷贝的对象都会开辟新的内存空间进行存储
import copy
print('----------------------------------------不可变类型-------------------------------')
# 不可变类型进行深拷贝,若子对象无可变类型,则不拷贝,只对引用(内存地址)拷贝
# 若子对象有可变类型,则对该对象到最后一个可变类型的每一层对象进行拷贝, 对每一层拷贝的对象都会开辟新的内存空间进行存储
print('-------------------数字------------------------')
a = 13
b = copy.deepcopy(a)
print('a值为:', a, 'a的内存地址为:', id(a), '\nb值为:', b, 'b的内存地址为:', id(b))
print('-------------------字符串-----------------------')
aa = 'abc'
bb = copy.deepcopy(aa)
print('aa值为:', aa, 'aa的内存地址为:', id(aa), '\nbb值为:', bb, 'bb的内存地址为:', id(bb))
print('--------------------元组------------------------')
a2 = (1, 3, ['a', 2])
b2 = copy.deepcopy(a2)
print('a2值为:', a2, 'a2的内存地址为:', id(a2), '\nb2值为:', b2, 'b2的内存地址为:', id(b2))
print('a2[2]值为:', a2[2], 'a2[2]的内存地址为:', id(a2[2]), '\nb2[2]值为:', b2[2], 'b2[2]的内存地址为:', id(b2[2]))
print('\nn---------------------------------可变类型-------------------------------------')
# 会对该对象到最后一个可变类型的每一层对象进行拷贝,对每一层拷贝的对象都会开辟新的内存空间进行存储
print('--------------------列表------------------------')
a3 = [1, 3, 5]
b3 = copy.deepcopy(a3)
print('a3值为:', a3, 'a3的内存地址为:', id(3), '\nb3值为:', b3, 'b3的内存地址为:', id(b3))
print('--------------------字典------------------------')
a4 = {'id': 2, 'name': 'xx', 'age': '18'}
b4 = copy.deepcopy(a4)
print('a4值为:', a4, 'a4的内存地址为:', id(a4), '\nb4值为:', b4, 'b4的内存地址为:', id(b4))
print('--------------------集合------------------------')
a5 ={1, 2, 3, 'aaa'}
b5 = copy.deepcopy(a5)
print('a5值为:', a5, 'a5的内存地址为:', id(a5), '\nb5值为:', b5, 'b5的内存地址为:', id(b5))
print('--------------------列表-----子对象--------------')
a6 = [1, 3, 5, [2, 4]]
b6 = copy.deepcopy(a6)
print('a6值为:', a6, 'a6的内存地址为:', id(a6), '\nb6值为:', b6, 'b6的内存地址为:', id(b6))  # 地址不同,只对第一层对象进行了拷贝,开辟了新空间
print('a6[3]值为:', a6[3], 'a6[3]的内存地址为:', id(a6[3]), '\nb6[3]值为:', b6[3], 'b3[3]的内存地址为:', id(b6[3]))  # 子对象内存地址不同,开辟了新空间
a6.append(6)
print('a6追加数据6后为:', a6, 'b6为:', b6)  # 只有a6追加了,b6未追加,说明不是同一个地址
a6[3][0] = 1
print('a6子对象修改后为:', a6, ',b6为:', b6)  # 只对a6的子对象进行了修改,说明子对象不是同一个地址

输出:
----------------------------------------不可变类型-------------------------------
-------------------数字------------------------
a值为: 13 a的内存地址为: 1465889784432 
b值为: 13 b的内存地址为: 1465889784432
-------------------字符串-----------------------
aa值为: abc aa的内存地址为: 1465891054128 
bb值为: abc bb的内存地址为: 1465891054128
--------------------元组------------------------
a2值为: (1, 3, ['a', 2]) a2的内存地址为: 1465894568256  # 含可变类型,内存地址不同,开辟了新空间
b2值为: (1, 3, ['a', 2]) b2的内存地址为: 1466174359296
a2[2]值为: ['a', 2] a2[2]的内存地址为: 1465890886592    # 子对象为可变类型,内存地址不同
b2[2]值为: ['a', 2] b2[2]的内存地址为: 1466173393536

n---------------------------------可变类型-------------------------------------
--------------------列表------------------------
a3值为: [1, 3, 5] a3的内存地址为: 1465889784112 
b3值为: [1, 3, 5] b3的内存地址为: 1466173181440
--------------------字典------------------------
a4值为: {'id': 2, 'name': 'xx', 'age': '18'} a4的内存地址为: 1465891094848 
b4值为: {'id': 2, 'name': 'xx', 'age': '18'} b4的内存地址为: 1465891094912
--------------------集合------------------------
a5值为: {'aaa', 1, 2, 3} a5的内存地址为: 1466174152416 
b5值为: {'aaa', 1, 2, 3} b5的内存地址为: 1466174152864
--------------------列表-----子对象--------------
a6值为: [1, 3, 5, [2, 4]] a6的内存地址为: 1466173392256 
b6值为: [1, 3, 5, [2, 4]] b6的内存地址为: 1466173392192
a6[3]值为: [2, 4] a6[3]的内存地址为: 1466173393216   # 子对象为可变类型,内存地址不同
b6[3]值为: [2, 4] b3[3]的内存地址为: 1466173392448
a6追加数据6后为: [1, 3, 5, [2, 4], 6] b6为: [1, 3, 5, [2, 4]]   # 内存不同,只追加给了a6
a6子对象修改后为: [1, 3, 5, [1, 4], 6] ,b6为: [1, 3, 5, [2, 4]]  # 因子对象内存不同,故只修改了a6的子对象

学习导航:http://xqnav.top/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

learning-striving

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值