折折叠叠像弹簧 发表于 2020-4-17 00:11:15

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,没有出现异常是因为字符串中元素被替换后,索引值还是保留的吗?还是说字符串的迭代机制不同?

sunrise085 发表于 2020-4-17 00:48:37

你理解的是对的。第二段程序因为没有改变字符串的索引顺序,所以能得出正确的结果。
但是不建议这么写,在循环中修改可迭代对象时,经常会出错。

txxcat 发表于 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进行的,不会受到元素被改变的困扰。

zltzlt 发表于 2020-4-17 08:11:14

1. 是这样,所以要加上拷贝。
lst =
for i in lst[:]:
      if item % 2 == 0:
                lst.remove(i)
print(lst)
2. 字符串是不可变对象,也就是说它的方法都不会改变自身,只会返回一个新的字符串。所以如果你单纯调用 replace() 却不接收 replace() 返回的字符串没用。

折折叠叠像弹簧 发表于 2020-4-17 10:11:12

zltzlt 发表于 2020-4-17 08:11
1. 是这样,所以要加上拷贝。

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

理解了!非常感谢你的回复!

折折叠叠像弹簧 发表于 2020-4-17 10:11:43

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

理解了!非常感谢你的回复!

折折叠叠像弹簧 发表于 2020-4-17 10:24:34

txxcat 发表于 2020-4-17 08:04
代码第4行没有缩进,修改之后运行的结果并非'Cappuccino',而是'32Cappuccino',究其原因是因为replace ...

谢谢你的细心回答!解释得很详细!!

折折叠叠像弹簧 发表于 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.capitalize()
print(lst)
输出正确
问题2: lst 是每循环一次都返回一个新的变量名吗?不太明白它和第一段代码中 i 的区别

问题3:有没有更简洁的方法来写循环体呢

txxcat 发表于 2020-4-26 01:28:26

折折叠叠像弹簧 发表于 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)

折折叠叠像弹簧 发表于 2020-4-26 14:24:51

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

明白了!!谢谢你
页: [1]
查看完整版本: python迭代过程中修改可迭代对象