鱼C论坛

 找回密码
 立即注册
查看: 2841|回复: 9

[已解决]python迭代过程中修改可迭代对象

[复制链接]
发表于 2020-4-17 00:11:15 | 显示全部楼层 |阅读模式

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

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

x
本帖最后由 折折叠叠像弹簧 于 2020-4-17 10:24 编辑

新手小白求教两个关于迭代问题的疑惑
迭代对象是列表时,迭代过程中删除列表元素可能会使结果出错

示例程序:删除列表中偶数
lst = [1,2,4,3,5]
for i in lst:
        if item % 2 == 0:
                lst.remove(i)
print(lst)

输出:
[1,4,3,5]
我猜测是for循环按索引值遍历,删除数字2时,数字4的索引值自动变为1,不再遍历,不知道对不对

-------------------------------------------------------   分割线    ------------------------------------------------------------

今天遇到了一个新情况:迭代对象是字符串时,迭代过程中把元素删除,却不会使结果出错
示例程序:删除字符串中非字母元素
str = '32Cappuccino'
for i in str:
        if i.isalpha() == False:
        str.replace(i,'')
print(str)

输出
Cappuccino

类推前述列表的情况,此时输出的应该是2Cappuccino,没有出现异常是因为字符串中元素被替换后,索引值还是保留的吗?还是说字符串的迭代机制不同?
最佳答案
2020-4-17 08:04:22
    代码第4行没有缩进,修改之后运行的结果并非'Cappuccino',而是'32Cappuccino',究其原因是因为replace是返回字符串的副本而不是修改字符串本身。我现在的版本是3.8.1,官方文档是这样的:
str.replace(old, new[, count])
返回字符串的副本,其中出现的所有子字符串 old 都将被替换为 new。 如果给出了可选参数 count,则只替换前 count 次出现。
    字符串在python里是不可修改的对象,所以实际上你无法对字符串对象本身进行任何修改,包括用分片、replace等方式处理的都是副本,那怕你赋值给原始的变量,那也是改变了id的存在,已经不是原始的对象了,所以你对字符串迭代不会受到任何影响。要想出现你说的结果,代码需要修改成这样(我加了id这样可以清晰看到改变):
str = '32Cappuccino'
print(id(str))
for i in str:
        if i.isalpha() == False:
            str=str.replace(i,'')  #把结果赋值给str
print(str)
print(id(str))

>>> %Run test22a.py
2693111182320
Cappuccino
2693111184432
   可以很明显地看到str的id已经改变了,而迭代则是根据原始的str进行的,不会受到元素被改变的困扰。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2020-4-17 00:48:37 | 显示全部楼层
你理解的是对的。第二段程序因为没有改变字符串的索引顺序,所以能得出正确的结果。
但是不建议这么写,在循环中修改可迭代对象时,经常会出错。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-4-17 08:04:22 | 显示全部楼层    本楼为最佳答案   
    代码第4行没有缩进,修改之后运行的结果并非'Cappuccino',而是'32Cappuccino',究其原因是因为replace是返回字符串的副本而不是修改字符串本身。我现在的版本是3.8.1,官方文档是这样的:
str.replace(old, new[, count])
返回字符串的副本,其中出现的所有子字符串 old 都将被替换为 new。 如果给出了可选参数 count,则只替换前 count 次出现。
    字符串在python里是不可修改的对象,所以实际上你无法对字符串对象本身进行任何修改,包括用分片、replace等方式处理的都是副本,那怕你赋值给原始的变量,那也是改变了id的存在,已经不是原始的对象了,所以你对字符串迭代不会受到任何影响。要想出现你说的结果,代码需要修改成这样(我加了id这样可以清晰看到改变):
str = '32Cappuccino'
print(id(str))
for i in str:
        if i.isalpha() == False:
            str=str.replace(i,'')  #把结果赋值给str
print(str)
print(id(str))

>>> %Run test22a.py
2693111182320
Cappuccino
2693111184432
   可以很明显地看到str的id已经改变了,而迭代则是根据原始的str进行的,不会受到元素被改变的困扰。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

发表于 2020-4-17 08:11:14 | 显示全部楼层
1. 是这样,所以要加上拷贝。
lst = [1,2,4,3,5]
for i in lst[:]:
        if item % 2 == 0:
                lst.remove(i)
print(lst)
2. 字符串是不可变对象,也就是说它的方法都不会改变自身,只会返回一个新的字符串。所以如果你单纯调用 replace() 却不接收 replace() 返回的字符串没用。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-4-17 10:11:12 | 显示全部楼层
zltzlt 发表于 2020-4-17 08:11
1. 是这样,所以要加上拷贝。

2. 字符串是不可变对象,也就是说它的方法都不会改变自身,只会返回一个新 ...

理解了!非常感谢你的回复!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-4-17 10:11:43 | 显示全部楼层
sunrise085 发表于 2020-4-17 00:48
你理解的是对的。第二段程序因为没有改变字符串的索引顺序,所以能得出正确的结果。
但是不建议这么写,在 ...

理解了!非常感谢你的回复!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-4-17 10:24:34 | 显示全部楼层
txxcat 发表于 2020-4-17 08:04
代码第4行没有缩进,修改之后运行的结果并非'Cappuccino',而是'32Cappuccino',究其原因是因为replace ...

谢谢你的细心回答!解释得很详细!!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-4-26 00:33:28 | 显示全部楼层
txxcat 发表于 2020-4-17 08:04
代码第4行没有缩进,修改之后运行的结果并非'Cappuccino',而是'32Cappuccino',究其原因是因为replace ...

鱼友你好,今天再次遇到关于字符串遍历的问题,向你请教

把列表中的单词变为首字母大写
lst = ['hello','world']
for i in lst:
    i = i.capitalize() #把生成字符串副本重新赋值
print(lst)

输出结果仍然是全部字母小写
问题1:推测是 i = i.capitalize() 这一步出问题,i不是一个变量名,所以生成的字符串副本没有被赋值,不知这样理解是否正确


修改代码
lst = ['hello','world']
for i in lst:
    lst[lst.index(i)] = lst[lst.index(i)].capitalize()
print(lst)
输出正确
问题2: lst[lst.index(i)] 是每循环一次都返回一个新的变量名吗?不太明白它和第一段代码中 i 的区别

问题3:有没有更简洁的方法来写循环体呢
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-4-26 01:28:26 | 显示全部楼层
折折叠叠像弹簧 发表于 2020-4-26 00:33
鱼友你好,今天再次遇到关于字符串遍历的问题,向你请教

把列表中的单词变为首字母大写

问题1:推测是 i = i.capitalize() 这一步出问题,i不是一个变量名,所以生成的字符串副本没有被赋值,不知这样理解是否正确
错误,i是由for循环生成的变量名,所以实际上赋值成功了,你在下面加上一句print(i)就可以看到,代码的问题是没有更新列表,列表还是老内容。

问题2: lst[lst.index(i)] 是每循环一次都返回一个新的变量名吗?不太明白它和第一段代码中 i 的区别
lst[lst.index(i)]是列表里的元素,这里的i和第一段的i是一样的东西,lst.index(i)获取元素在列表的中序号,例如'hello'的在列表的序号是0,所以就是lst[0],指列表lst的第一个元素,改变这个的内容就会更新列表了,这样就解决了第一段代码的问题。

问题3:有没有更简洁的方法来写循环体呢
可以直接使用序号来让代码更容易理解一些:
lst = ['hello','world']
i=0
for i in range(len(lst)):
    lst[i] = lst[i].capitalize()
    i+=1
print(lst)
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-4-26 14:24:51 | 显示全部楼层
txxcat 发表于 2020-4-26 01:28
问题1:推测是 i = i.capitalize() 这一步出问题,i不是一个变量名,所以生成的字符串副本没有被赋值,不 ...

明白了!!谢谢你
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-1-11 22:46

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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