鱼C论坛

 找回密码
 立即注册
查看: 1750|回复: 8

[已解决]关于for循环的一些疑惑

[复制链接]
发表于 2018-1-28 22:26:25 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
如图中的这两次for循环,为什么第一次a列表没有发生改变,第二次发生了改变呢? 迭代1.PNG
我第一次以为是如果直接用each in 列表,每次操作的each都是对原列表元素的一个副本,而非元素本身,所以不会对列表本身造成影响,可是我这次的尝试又让我疑惑了 迭代2.PNG
这次我先创建aa列表,接着用bb=aa[:]的方法,按照我一开始的理解,bb是aa的副本,再对bb操作,aa应该不受影响,但是…… 迭代3.PNG
还有这一次,我用for each in bb循环,还对each做了一些操作,为什么可以做aa.remove(each),照理说aa里不应该找不到操作之后的each吗?如下图 迭代4.PNG
最佳答案
2018-1-29 22:36:51
以代码方式发的,你可以哪来运行下 在看看理解  如果还有不清楚欢迎联系!
写了一个多小时(好累),没怎么写种东西,写的差多多包涵
如果帮助你理解了,希望给个最佳
  1. # 我们先定义4个样例列表
  2. a = [x for x in range(10)]
  3. b = a[:]
  4. c = list(map(lambda x: [x, x + 1], range(0, 10, 2)))
  5. d = c[:]
  6. print(a, '\n', b, '\n', c, '\n', d)


  7. # 查看各个列表及其中的元素在内存中的id 看Python的处理方式
  8. # 查看名字的地址
  9. print(id(a), id(b), id(c), id(d))

  10. # 查看列表内元素的地址
  11. for i in range(5):
  12.     print(id(a[i]), id(b[i]))

  13. print('----------------------------------')
  14. for i in range(5):
  15.     for j in range(2):
  16.         print(id(c[i][j]), id(d[i][j]))

  17. # 我们修改下值来看看
  18. print(id(a[0]))
  19. a[0] = 6
  20. print(id(a[0]))
  21. # 我们知道a[6] = 6,我们来看看a[7]
  22. print(id(a[6]))

  23. # 每次的运行结果都一样,且值相同的变量指向也是同一个内存地址
  24. # 这里我们甚至可以预测 我们给一个整数赋值 不用id都可以算出其内存地址
  25. # 据此猜测Python中对某些数据并非是创建时随机分配可用的内存地址并写入值,而是将变量名指向已经存在的值

  26. # 看懂上面的例子后,我们再来看for循环的实现
  27. # 我们先将a[0]的值改回来
  28. a[0] = 0
  29. print('-------------下面是测试1-------------------')
  30. count = 0
  31. for each in a:
  32.     print(id(each), id(a[count]))
  33.     each += 1
  34.     print(id(each), id(a[count]))
  35.     print('-------------------------------')
  36.     count += 1
  37. # 是不是前后地址不一致

  38. print('-------------下面是测试2-------------------')
  39. count = 0
  40. for each in c:
  41.     print(id(each), id(c[count]))
  42.     print(id(each[0]), id(c[count][0]))
  43.     each[0] += 1
  44.     print(id(each), id(c[count]))
  45.     print(id(each[0]), id(c[count][0]))
  46.     print('-------------------------------')
  47.     count += 1
  48. # 为什么这里前后地址一致呢!
  49. # 希望你对指针和地址的概念有一定的了解
  50. '''
  51. 1、Python中没有变量,都是标签(我们可以认为访问一个标签即是根据标签内存储的地址来访问此地址里的值,类似于C中的指针)
  52.   C语言数组 s的地址即是s[0]的地址(这里指一维数组)
  53.   但从例子中我们看到id(a)和id(a[0])完全不一样     (在我看来即一个存值,一个存地址)
  54. 2、列表类似 当我们切片一个列表时即在不同的内存新建了一个标签  
  55. (用'='则是公用同一个地址  b = a[:] c = a 则id(a) == id(c) != id(b),即赋值实质上是同一个,即一个人的两个名字)
  56. 3、一维数组和二维数组结果不同即是这种存储方式决定的
  57. 数组中a[0]……存储的是其值的地址  
  58. 一维数组即是值的地址,二维数组既是另一个一维数组的地址
  59. 对一维数组,当for each in a时each和a[0]、a[1]……指向同一个地址,即值得地址,但本身不同  改变each只是改变each里的地址即指向,对a没有影响
  60. 对二维数组,当for each in a时each和a[0]、a[1]……指向同一个地址,但地址处仍是一个一维列表,这个一维列表存储的是各个值得地址,
  61. 当你改变each[0]、each[1]……时,改变的是其中地址的指向,与上面说的一维数组相同
  62. >>>>>>>但是,each和a[0]、a[1]……扔指向这个一维数组,第一次指向没有改变
  63. >>>>>>>所以,当你再次访问a这个列表时,会先根据地址跳到其中的一维列表,再根据一维列表的内的地址跳到数值  因为一维列表里的地址已经改变,所以a的值也会改变
  64. >>>>>>>而切边只是对第一层的另建一个标签,其中第二层的指向不变,所以b列表的值也会改变
  65. '''
  66. '''
  67. 语言表达能力比较差,不知道能不能理解,如果不太理解可以根据第3点画一个指向图,可能比较好理解
  68. 谢谢!!!
  69. '''
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2018-1-28 23:24:06 | 显示全部楼层
图1 each访问每个元素更像是只读型,你对each修改,each指向了别处,a没有变动id,没有改到a=[1,2,3]
图2 each变化,aa,bb每个元素也跟着变化地址指向,只能从地址结果分析是这么变得
1.png
2.png
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-1-28 23:36:15 | 显示全部楼层
塔利班 发表于 2018-1-28 23:24
图1 each访问每个元素更像是只读型,你对each修改,each指向了别处,a没有变动id,没有改到a=[1,2,3]
图2  ...

图2没沾好,程序又被我搞没了== ,
你的each in bb循环里,aa[0][0]和each[0]一直一个id,所以一起改变,
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-1-29 09:13:40 | 显示全部楼层
塔利班 发表于 2018-1-28 23:36
图2没沾好,程序又被我搞没了== ,
你的each in bb循环里,aa[0][0]和each[0]一直一个id,所以一起改变,

我也试过了   这是否说明Python不支持二维的切片拷贝,就算用copy()函数也不可以,aa和bb也会一起改变,只有分别定义aa = [[1,2],[3,4],[5,6]];  bb = [[1,2],[3,4],[5,6]]才会不跟着一起改变
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-1-29 16:08:49 | 显示全部楼层
ColbySuns 发表于 2018-1-29 09:13
我也试过了   这是否说明Python不支持二维的切片拷贝,就算用copy()函数也不可以,aa和bb也会一起改变 ...

只能说机制的问题,现在学到了,小甲鱼的书上也没有告诉
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-1-29 22:36:51 | 显示全部楼层    本楼为最佳答案   
以代码方式发的,你可以哪来运行下 在看看理解  如果还有不清楚欢迎联系!
写了一个多小时(好累),没怎么写种东西,写的差多多包涵
如果帮助你理解了,希望给个最佳
  1. # 我们先定义4个样例列表
  2. a = [x for x in range(10)]
  3. b = a[:]
  4. c = list(map(lambda x: [x, x + 1], range(0, 10, 2)))
  5. d = c[:]
  6. print(a, '\n', b, '\n', c, '\n', d)


  7. # 查看各个列表及其中的元素在内存中的id 看Python的处理方式
  8. # 查看名字的地址
  9. print(id(a), id(b), id(c), id(d))

  10. # 查看列表内元素的地址
  11. for i in range(5):
  12.     print(id(a[i]), id(b[i]))

  13. print('----------------------------------')
  14. for i in range(5):
  15.     for j in range(2):
  16.         print(id(c[i][j]), id(d[i][j]))

  17. # 我们修改下值来看看
  18. print(id(a[0]))
  19. a[0] = 6
  20. print(id(a[0]))
  21. # 我们知道a[6] = 6,我们来看看a[7]
  22. print(id(a[6]))

  23. # 每次的运行结果都一样,且值相同的变量指向也是同一个内存地址
  24. # 这里我们甚至可以预测 我们给一个整数赋值 不用id都可以算出其内存地址
  25. # 据此猜测Python中对某些数据并非是创建时随机分配可用的内存地址并写入值,而是将变量名指向已经存在的值

  26. # 看懂上面的例子后,我们再来看for循环的实现
  27. # 我们先将a[0]的值改回来
  28. a[0] = 0
  29. print('-------------下面是测试1-------------------')
  30. count = 0
  31. for each in a:
  32.     print(id(each), id(a[count]))
  33.     each += 1
  34.     print(id(each), id(a[count]))
  35.     print('-------------------------------')
  36.     count += 1
  37. # 是不是前后地址不一致

  38. print('-------------下面是测试2-------------------')
  39. count = 0
  40. for each in c:
  41.     print(id(each), id(c[count]))
  42.     print(id(each[0]), id(c[count][0]))
  43.     each[0] += 1
  44.     print(id(each), id(c[count]))
  45.     print(id(each[0]), id(c[count][0]))
  46.     print('-------------------------------')
  47.     count += 1
  48. # 为什么这里前后地址一致呢!
  49. # 希望你对指针和地址的概念有一定的了解
  50. '''
  51. 1、Python中没有变量,都是标签(我们可以认为访问一个标签即是根据标签内存储的地址来访问此地址里的值,类似于C中的指针)
  52.   C语言数组 s的地址即是s[0]的地址(这里指一维数组)
  53.   但从例子中我们看到id(a)和id(a[0])完全不一样     (在我看来即一个存值,一个存地址)
  54. 2、列表类似 当我们切片一个列表时即在不同的内存新建了一个标签  
  55. (用'='则是公用同一个地址  b = a[:] c = a 则id(a) == id(c) != id(b),即赋值实质上是同一个,即一个人的两个名字)
  56. 3、一维数组和二维数组结果不同即是这种存储方式决定的
  57. 数组中a[0]……存储的是其值的地址  
  58. 一维数组即是值的地址,二维数组既是另一个一维数组的地址
  59. 对一维数组,当for each in a时each和a[0]、a[1]……指向同一个地址,即值得地址,但本身不同  改变each只是改变each里的地址即指向,对a没有影响
  60. 对二维数组,当for each in a时each和a[0]、a[1]……指向同一个地址,但地址处仍是一个一维列表,这个一维列表存储的是各个值得地址,
  61. 当你改变each[0]、each[1]……时,改变的是其中地址的指向,与上面说的一维数组相同
  62. >>>>>>>但是,each和a[0]、a[1]……扔指向这个一维数组,第一次指向没有改变
  63. >>>>>>>所以,当你再次访问a这个列表时,会先根据地址跳到其中的一维列表,再根据一维列表的内的地址跳到数值  因为一维列表里的地址已经改变,所以a的值也会改变
  64. >>>>>>>而切边只是对第一层的另建一个标签,其中第二层的指向不变,所以b列表的值也会改变
  65. '''
  66. '''
  67. 语言表达能力比较差,不知道能不能理解,如果不太理解可以根据第3点画一个指向图,可能比较好理解
  68. 谢谢!!!
  69. '''
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 2 反对 0

使用道具 举报

发表于 2018-1-29 23:42:15 | 显示全部楼层
太阳花田 发表于 2018-1-29 22:36
以代码方式发的,你可以哪来运行下 在看看理解  如果还有不清楚欢迎联系!
写了一个多小时(好累{:10_266: ...

学到了  理解了  谢谢
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-1-30 12:41:00 | 显示全部楼层
太阳花田 发表于 2018-1-29 22:36
以代码方式发的,你可以哪来运行下 在看看理解  如果还有不清楚欢迎联系!
写了一个多小时(好累{:10_266: ...

原来如此.....和C++的引用差不多啊
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-1-31 08:55:04 | 显示全部楼层
太阳花田 发表于 2018-1-29 22:36
以代码方式发的,你可以哪来运行下 在看看理解  如果还有不清楚欢迎联系!
写了一个多小时(好累{:10_266: ...

这几天在考驾照,没有上论坛,抱歉今天才看到
谢谢你的讲解,我明白了,辛苦鱼油啦,撒花撒花~~~
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2026-3-6 12:55

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表