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


    一、生成器

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

    创建生成器方式

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

    1.1 生成器推导式

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

    • next()函数:用来获取生成器中下一个值,从第一个开始获取
    • for循环:遍历生成器中每一个元素值,若其前已用next()获取,则从next()获取的元素后开始获取
    1. generator = (i**2 for i in range(6)) # 创建生成器
    2. print(generator)
    3. print('生成器中下一个值为:', next(generator), '再下一个值为:', next(generator))
    4. for v in generator: # 用一个生成一个,从next()之后继续生成
    5. print('遍历生成器,剩余值依次为:', v)
    6. 输出:
    7. object at 0x000001BB6E8D2FF0>
    8. 生成器中下一个值为: 0 再下一个值为: 1
    9. 遍历生成器,剩余值依次为: 4
    10. 遍历生成器,剩余值依次为: 9
    11. 遍历生成器,剩余值依次为: 16
    12. 遍历生成器,剩余值依次为: 25

    1.2 yield关键字

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

    • 代码执行到yield时会暂停,然后将结果返回出去,下次启动生成器会在暂停的位置继续往下执行
    • 生成器如果把数据生成完成,再次获取生成器中的下一个数据会抛出一个StopIteration 异常,表示停止迭代异常
    • while 循环内部没有处理异常操作,需手动添加处理异常操作
    • for 循环内部自动处理了停止迭代异常,使用更方便,推荐使用
    1. def generator(): # 函数中有yield关键字,则该函数即为生成器
    2. for i in range(6):
    3. print('开始生成数据')
    4. yield i + 2 # 执行到此处时,程序暂停执行,并将结果返回,当再次启动生成器时继续从此处往下执行
    5. print('完成一次数据生成')
    6. result = generator()
    7. print('result类型为:', type(result))
    8. print('生成器中下一个值为:', next(result))
    9. print('--------------------------------')
    10. print('生成器中再下一个值为:', next(result))
    11. # 遍历生成器方式1,for循环
    12. # for i in result: # for循环自动处理停止迭代异常
    13. # print('遍历生成器,剩余值依次为:', i)
    14. # 遍历生成器方式2,while循环
    15. # while True:
    16. # try:
    17. # print('遍历生成器,剩余值依次为:', next(result))
    18. # except StopIteration as e: # 处理迭代异常,生成最后一个数据时,会产生StopIteration异常
    19. # break
    20. 输出:
    21. result类型为: <class 'generator'>
    22. 开始生成数据
    23. 生成器中下一个值为: 2
    24. --------------------------------
    25. 完成一次数据生成
    26. 开始生成数据
    27. 生成器中再下一个值为: 3

    1.3 生成器应用场景

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

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

    1. def fibonacci(n): # n为生成个数
    2. a = 0 # 初始化前两个值
    3. b = 1
    4. i = 0 # 记录生成个数的索引
    5. while i < n:
    6. result = a
    7. a, b = b, a + b # 交换变量间的值
    8. i += 1
    9. yield result # 代码执行到yield会暂停,然后把结果返回出去,下次启动生成器会在暂停的位置继续往下执行
    10. generator = fibonacci(20) # 创建生成器
    11. list = []
    12. for v in generator:
    13. list.append(v)
    14. print('生成器生成的斐波那契数列值依次为:', list)
    15. 输出:
    16. 生成器生成的斐波那契数列值依次为: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181]

    二、浅拷贝与深拷贝

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

    2.1浅拷贝

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

    • 不可变类型:数字、字符串、元组,浅拷贝不会给拷贝的对象开辟新的内存空间,而只是拷贝了这个对象的引用
    • 可变类型:列表、字典、集合,浅拷贝只对可变类型的第一层对象进行拷贝,对拷贝的对象会开辟新的内存空间进行存储,子对象不进行拷贝
    1. import copy
    2. print('----------------------------------------不可变类型-------------------------------')
    3. # 拷贝后内存地址未变,说明未对对象进行拷贝,即浅拷贝不会对不可变类型的拷贝开辟新的内存空间
    4. # 对不可变类型的浅拷贝实际是对引用(内存地址)的拷贝,两个变量指向同一个内存地址
    5. print('-------------------数字------------------------')
    6. a = 13
    7. b = copy.copy(a)
    8. print('a值为:', a, 'a的内存地址为:', id(a), '\nb值为:', b, 'b的内存地址为:', id(b))
    9. print('-------------------字符串-----------------------')
    10. aa = 'abc'
    11. bb = copy.copy(aa)
    12. print('aa值为:', aa, 'aa的内存地址为:', id(aa), '\nbb值为:', bb, 'bb的内存地址为:', id(bb))
    13. print('--------------------元组------------------------')
    14. a2 = (1, 3, ['a', 2])
    15. b2 = copy.copy(a2)
    16. print('a2值为:', a2, 'a2的内存地址为:', id(a2), '\nb2值为:', b2, 'b2的内存地址为:', id(b2))
    17. print('a2[2]值为:', a2[2], 'a2[2]的内存地址为:', id(a2[2]), '\nb2[2]值为:', b2[2], 'b2[2]的内存地址为:', id(b2[2]))
    18. print('\n---------------------------------可变类型-------------------------------------')
    19. # 只对可变类型第一层对象进行拷贝,开辟新的内存空间
    20. # 不会拷贝最外层对象内的子对象
    21. print('--------------------列表------------------------')
    22. a3 = [1, 3, 5]
    23. b3 = copy.copy(a3)
    24. print('a3值为:', a3, 'a3的内存地址为:', id(3), '\nb3值为:', b3, 'b3的内存地址为:', id(b3))
    25. print('--------------------字典------------------------')
    26. a4 = {'id': 2, 'name': 'xx', 'age': '18'}
    27. b4 = copy.copy(a4)
    28. print('a4值为:', a4, 'a4的内存地址为:', id(a4), '\nb4值为:', b4, 'b4的内存地址为:', id(b4))
    29. print('--------------------集合------------------------')
    30. a5 ={1, 2, 3, 'aaa'}
    31. b5 = copy.copy(a5)
    32. print('a5值为:', a5, 'a5的内存地址为:', id(a5), '\nb5值为:', b5, 'b5的内存地址为:', id(b5))
    33. print('--------------------列表-----子对象--------------')
    34. a6 = [1, 3, 5, [2, 4]]
    35. b6 = copy.copy(a6)
    36. print('a6值为:', a6, 'a6的内存地址为:', id(a6), '\nb6值为:', b6, 'b6的内存地址为:', id(b6)) # 地址不同,只对第一层对象进行了拷贝,开辟了新空间
    37. print('a6[3]值为:', a6[3], 'a6[3]的内存地址为:', id(a6[3]), '\nb6[3]值为:', b6[3], 'b3[3]的内存地址为:', id(b6[3])) # 子对象内存地址不变,说明不会拷贝子对象
    38. a6.append(6)
    39. print('a6追加数据6后为:', a6, 'b6为:', b6) # 只有a6追加了,b6未追加,说明不是同一个地址
    40. a6[3][0] = 1
    41. print('a6子对象修改后为:', a6, ',b6为:', b6) # a6和b6的子对象均进行了修改,说明子对象是同一个地址
    42. 输出:
    43. ----------------------------------------不可变类型-------------------------------
    44. -------------------数字------------------------
    45. a值为: 13 a的内存地址为: 1938320720496
    46. b值为: 13 b的内存地址为: 1938320720496
    47. -------------------字符串-----------------------
    48. aa值为: abc aa的内存地址为: 1938321990192
    49. bb值为: abc bb的内存地址为: 1938321990192
    50. --------------------元组------------------------
    51. a2值为: (1, 3, ['a', 2]) a2的内存地址为: 1938325111232
    52. b2值为: (1, 3, ['a', 2]) b2的内存地址为: 1938325111232
    53. a2[2]值为: ['a', 2] a2[2]的内存地址为: 2211555234752 # 子对象内存地址相同,未开辟新空间
    54. b2[2]值为: ['a', 2] b2[2]的内存地址为: 2211555234752
    55. n---------------------------------可变类型-------------------------------------
    56. --------------------列表------------------------
    57. a3值为: [1, 3, 5] a3的内存地址为: 1938320720176
    58. b3值为: [1, 3, 5] b3的内存地址为: 1938604134016
    59. --------------------字典------------------------
    60. a4值为: {'id': 2, 'name': 'xx', 'age': '18'} a4的内存地址为: 1938322030912
    61. b4值为: {'id': 2, 'name': 'xx', 'age': '18'} b4的内存地址为: 1938322031168
    62. --------------------集合------------------------
    63. a5值为: {'aaa', 1, 2, 3} a5的内存地址为: 1938604957408
    64. b5值为: {'aaa', 1, 2, 3} b5的内存地址为: 1938604957856
    65. --------------------列表-----子对象--------------
    66. a6值为: [1, 3, 5, [2, 4]] a6的内存地址为: 1938604134528
    67. b6值为: [1, 3, 5, [2, 4]] b6的内存地址为: 1938604134208
    68. a6[3]值为: [2, 4] a6[3]的内存地址为: 1938604134976 # 子对象内存地址相同,未开辟新空间
    69. b6[3]值为: [2, 4] b3[3]的内存地址为: 1938604134976
    70. a6追加数据6后为: [1, 3, 5, [2, 4], 6] b6为: [1, 3, 5, [2, 4]]
    71. a6子对象修改后为: [1, 3, 5, [1, 4], 6] ,b6为: [1, 3, 5, [1, 4]]

    2.2 深拷贝

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

    不可变类型

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

    可变类型

    • 会对该对象到最后一个可变类型的每一层对象进行拷贝,对每一层拷贝的对象都会开辟新的内存空间进行存储
    1. import copy
    2. print('----------------------------------------不可变类型-------------------------------')
    3. # 不可变类型进行深拷贝,若子对象无可变类型,则不拷贝,只对引用(内存地址)拷贝
    4. # 若子对象有可变类型,则对该对象到最后一个可变类型的每一层对象进行拷贝, 对每一层拷贝的对象都会开辟新的内存空间进行存储
    5. print('-------------------数字------------------------')
    6. a = 13
    7. b = copy.deepcopy(a)
    8. print('a值为:', a, 'a的内存地址为:', id(a), '\nb值为:', b, 'b的内存地址为:', id(b))
    9. print('-------------------字符串-----------------------')
    10. aa = 'abc'
    11. bb = copy.deepcopy(aa)
    12. print('aa值为:', aa, 'aa的内存地址为:', id(aa), '\nbb值为:', bb, 'bb的内存地址为:', id(bb))
    13. print('--------------------元组------------------------')
    14. a2 = (1, 3, ['a', 2])
    15. b2 = copy.deepcopy(a2)
    16. print('a2值为:', a2, 'a2的内存地址为:', id(a2), '\nb2值为:', b2, 'b2的内存地址为:', id(b2))
    17. print('a2[2]值为:', a2[2], 'a2[2]的内存地址为:', id(a2[2]), '\nb2[2]值为:', b2[2], 'b2[2]的内存地址为:', id(b2[2]))
    18. print('\nn---------------------------------可变类型-------------------------------------')
    19. # 会对该对象到最后一个可变类型的每一层对象进行拷贝,对每一层拷贝的对象都会开辟新的内存空间进行存储
    20. print('--------------------列表------------------------')
    21. a3 = [1, 3, 5]
    22. b3 = copy.deepcopy(a3)
    23. print('a3值为:', a3, 'a3的内存地址为:', id(3), '\nb3值为:', b3, 'b3的内存地址为:', id(b3))
    24. print('--------------------字典------------------------')
    25. a4 = {'id': 2, 'name': 'xx', 'age': '18'}
    26. b4 = copy.deepcopy(a4)
    27. print('a4值为:', a4, 'a4的内存地址为:', id(a4), '\nb4值为:', b4, 'b4的内存地址为:', id(b4))
    28. print('--------------------集合------------------------')
    29. a5 ={1, 2, 3, 'aaa'}
    30. b5 = copy.deepcopy(a5)
    31. print('a5值为:', a5, 'a5的内存地址为:', id(a5), '\nb5值为:', b5, 'b5的内存地址为:', id(b5))
    32. print('--------------------列表-----子对象--------------')
    33. a6 = [1, 3, 5, [2, 4]]
    34. b6 = copy.deepcopy(a6)
    35. print('a6值为:', a6, 'a6的内存地址为:', id(a6), '\nb6值为:', b6, 'b6的内存地址为:', id(b6)) # 地址不同,只对第一层对象进行了拷贝,开辟了新空间
    36. print('a6[3]值为:', a6[3], 'a6[3]的内存地址为:', id(a6[3]), '\nb6[3]值为:', b6[3], 'b3[3]的内存地址为:', id(b6[3])) # 子对象内存地址不同,开辟了新空间
    37. a6.append(6)
    38. print('a6追加数据6后为:', a6, 'b6为:', b6) # 只有a6追加了,b6未追加,说明不是同一个地址
    39. a6[3][0] = 1
    40. print('a6子对象修改后为:', a6, ',b6为:', b6) # 只对a6的子对象进行了修改,说明子对象不是同一个地址
    41. 输出:
    42. ----------------------------------------不可变类型-------------------------------
    43. -------------------数字------------------------
    44. a值为: 13 a的内存地址为: 1465889784432
    45. b值为: 13 b的内存地址为: 1465889784432
    46. -------------------字符串-----------------------
    47. aa值为: abc aa的内存地址为: 1465891054128
    48. bb值为: abc bb的内存地址为: 1465891054128
    49. --------------------元组------------------------
    50. a2值为: (1, 3, ['a', 2]) a2的内存地址为: 1465894568256 # 含可变类型,内存地址不同,开辟了新空间
    51. b2值为: (1, 3, ['a', 2]) b2的内存地址为: 1466174359296
    52. a2[2]值为: ['a', 2] a2[2]的内存地址为: 1465890886592 # 子对象为可变类型,内存地址不同
    53. b2[2]值为: ['a', 2] b2[2]的内存地址为: 1466173393536
    54. n---------------------------------可变类型-------------------------------------
    55. --------------------列表------------------------
    56. a3值为: [1, 3, 5] a3的内存地址为: 1465889784112
    57. b3值为: [1, 3, 5] b3的内存地址为: 1466173181440
    58. --------------------字典------------------------
    59. a4值为: {'id': 2, 'name': 'xx', 'age': '18'} a4的内存地址为: 1465891094848
    60. b4值为: {'id': 2, 'name': 'xx', 'age': '18'} b4的内存地址为: 1465891094912
    61. --------------------集合------------------------
    62. a5值为: {'aaa', 1, 2, 3} a5的内存地址为: 1466174152416
    63. b5值为: {'aaa', 1, 2, 3} b5的内存地址为: 1466174152864
    64. --------------------列表-----子对象--------------
    65. a6值为: [1, 3, 5, [2, 4]] a6的内存地址为: 1466173392256
    66. b6值为: [1, 3, 5, [2, 4]] b6的内存地址为: 1466173392192
    67. a6[3]值为: [2, 4] a6[3]的内存地址为: 1466173393216 # 子对象为可变类型,内存地址不同
    68. b6[3]值为: [2, 4] b3[3]的内存地址为: 1466173392448
    69. a6追加数据6后为: [1, 3, 5, [2, 4], 6] b6为: [1, 3, 5, [2, 4]] # 内存不同,只追加给了a6
    70. a6子对象修改后为: [1, 3, 5, [1, 4], 6] ,b6为: [1, 3, 5, [2, 4]] # 因子对象内存不同,故只修改了a6的子对象

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

  • 相关阅读:
    算法小记【1】
    个人网页设计成品DW静态网页 HTML网页设计结课作业 web课程设计网页规划与设计 Web大学生个人网页成品 web网页设计期末课程大作业
    事件绑定-回调函数
    从零开始的C++(十四)
    异步http和同步http原理和差异
    一文彻底搞懂性能测试
    基于OpenCV的轮廓检测(2)
    ddd领域模型落地难
    Linux常见命令
    【LLM】0x00 大模型简介
  • 原文地址:https://blog.csdn.net/qq_43874317/article/details/127848165