python迭代过程中修改可迭代对象
本帖最后由 折折叠叠像弹簧 于 2020-4-17 10:24 编辑新手小白求教两个关于迭代问题的疑惑
迭代对象是列表时,迭代过程中删除列表元素可能会使结果出错
示例程序:删除列表中偶数
lst =
for i in lst:
if item % 2 == 0:
lst.remove(i)
print(lst)
输出:
我猜测是for循环按索引值遍历,删除数字2时,数字4的索引值自动变为1,不再遍历,不知道对不对
------------------------------------------------------- 分割线 ------------------------------------------------------------
今天遇到了一个新情况:迭代对象是字符串时,迭代过程中把元素删除,却不会使结果出错
示例程序:删除字符串中非字母元素
str = '32Cappuccino'
for i in str:
if i.isalpha() == False:
str.replace(i,'')
print(str)
输出
Cappuccino
类推前述列表的情况,此时输出的应该是2Cappuccino,没有出现异常是因为字符串中元素被替换后,索引值还是保留的吗?还是说字符串的迭代机制不同? 你理解的是对的。第二段程序因为没有改变字符串的索引顺序,所以能得出正确的结果。
但是不建议这么写,在循环中修改可迭代对象时,经常会出错。 代码第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进行的,不会受到元素被改变的困扰。
1. 是这样,所以要加上拷贝。
lst =
for i in lst[:]:
if item % 2 == 0:
lst.remove(i)
print(lst)
2. 字符串是不可变对象,也就是说它的方法都不会改变自身,只会返回一个新的字符串。所以如果你单纯调用 replace() 却不接收 replace() 返回的字符串没用。 zltzlt 发表于 2020-4-17 08:11
1. 是这样,所以要加上拷贝。
2. 字符串是不可变对象,也就是说它的方法都不会改变自身,只会返回一个新 ...
理解了!非常感谢你的回复! sunrise085 发表于 2020-4-17 00:48
你理解的是对的。第二段程序因为没有改变字符串的索引顺序,所以能得出正确的结果。
但是不建议这么写,在 ...
理解了!非常感谢你的回复! txxcat 发表于 2020-4-17 08:04
代码第4行没有缩进,修改之后运行的结果并非'Cappuccino',而是'32Cappuccino',究其原因是因为replace ...
谢谢你的细心回答!解释得很详细!! 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.capitalize()
print(lst)
输出正确
问题2: lst 是每循环一次都返回一个新的变量名吗?不太明白它和第一段代码中 i 的区别
问题3:有没有更简洁的方法来写循环体呢 折折叠叠像弹簧 发表于 2020-4-26 00:33
鱼友你好,今天再次遇到关于字符串遍历的问题,向你请教
把列表中的单词变为首字母大写
问题1:推测是 i = i.capitalize() 这一步出问题,i不是一个变量名,所以生成的字符串副本没有被赋值,不知这样理解是否正确
错误,i是由for循环生成的变量名,所以实际上赋值成功了,你在下面加上一句print(i)就可以看到,代码的问题是没有更新列表,列表还是老内容。
问题2: lst 是每循环一次都返回一个新的变量名吗?不太明白它和第一段代码中 i 的区别
lst是列表里的元素,这里的i和第一段的i是一样的东西,lst.index(i)获取元素在列表的中序号,例如'hello'的在列表的序号是0,所以就是lst,指列表lst的第一个元素,改变这个的内容就会更新列表了,这样就解决了第一段代码的问题。
问题3:有没有更简洁的方法来写循环体呢
可以直接使用序号来让代码更容易理解一些:
lst = ['hello','world']
i=0
for i in range(len(lst)):
lst = lst.capitalize()
i+=1
print(lst) txxcat 发表于 2020-4-26 01:28
问题1:推测是 i = i.capitalize() 这一步出问题,i不是一个变量名,所以生成的字符串副本没有被赋值,不 ...
明白了!!谢谢你
页:
[1]