循环“忘记”以删除一些项目[重复]
- 2024-11-28 08:37:00
- admin 原创
- 8
问题描述:
在此代码中,我尝试创建一个函数 anti_vowel,该函数将从字符串中删除所有元音 (aeiouAEIOU)。我认为它应该可以正常工作,但是当我运行它时,示例文本“Hey look Words!”被返回为“Hy lk Words!”。它“忘记”删除最后一个“o”。怎么会这样?
text = "Hey look Words!"
def anti_vowel(text):
textlist = list(text)
for char in textlist:
if char.lower() in 'aeiou':
textlist.remove(char)
return "".join(textlist)
print anti_vowel(text)
解决方案 1:
您正在修改迭代的列表,这必然会导致一些不直观的行为。相反,请复制列表,这样您就不会从迭代的内容中删除元素。
for char in textlist[:]: #shallow copy of the list
# etc
为了澄清您看到的行为,请查看此内容。将print char, textlist
其放在 (原始) 循环的开头。您可能希望这会在列表旁边垂直打印出您的字符串,但您实际上会得到以下内容:
H ['H', 'e', 'y', ' ', 'l', 'o', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
e ['H', 'e', 'y', ' ', 'l', 'o', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
['H', 'y', ' ', 'l', 'o', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!'] # !
l ['H', 'y', ' ', 'l', 'o', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
o ['H', 'y', ' ', 'l', 'o', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
k ['H', 'y', ' ', 'l', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!'] # Problem!!
['H', 'y', ' ', 'l', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
W ['H', 'y', ' ', 'l', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
o ['H', 'y', ' ', 'l', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
d ['H', 'y', ' ', 'l', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
s ['H', 'y', ' ', 'l', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
! ['H', 'y', ' ', 'l', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
Hy lk Words!
那么到底发生了什么?for x in y
Python 中的 nice 循环实际上只是语法糖:它仍然通过索引访问列表元素。因此,当您在迭代列表中删除元素时,您会开始跳过值(如上所示)。结果,您永远不会看到o
中的第二个"look"
;您跳过它是因为当您删除前一个元素时索引已经“超过”了它。然后,当您到达 中时o
,"Words"
您将删除 的第一个出现'o'
,即您之前跳过的那个。
正如其他人提到的,列表推导式可能是一种更好(更干净、更清晰)的方法。利用 Python 字符串可迭代的事实:
def remove_vowels(text): # function names should start with verbs! :)
return ''.join(ch for ch in text if ch.lower() not in 'aeiou')
解决方案 2:
其他答案会告诉您为什么for
在更改列表时会跳过项目。此答案会告诉您如何在不使用显式循环的情况下删除字符串中的字符。
使用str.translate()
:
vowels = 'aeiou'
vowels += vowels.upper()
text.translate(None, vowels)
这将删除第二个参数中列出的所有字符。
演示:
>>> text = "Hey look Words!"
>>> vowels = 'aeiou'
>>> vowels += vowels.upper()
>>> text.translate(None, vowels)
'Hy lk Wrds!'
>>> text = 'The Quick Brown Fox Jumps Over The Lazy Fox'
>>> text.translate(None, vowels)
'Th Qck Brwn Fx Jmps vr Th Lzy Fx'
在 Python 3 中,该str.translate()
方法(Python 2 unicode.translate()
:)的不同之处在于它不接受deletechars参数;第一个参数是一个将 Unicode 序数(整数值)映射到新值的字典。用于None
需要删除的任何字符:
# Python 3 code
vowels = 'aeiou'
vowels += vowels.upper()
vowels_table = dict.fromkeys(map(ord, vowels))
text.translate(vowels_table)
您还可以使用str.maketrans()
静态方法来生成该映射:
vowels = 'aeiou'
vowels += vowels.upper()
text.translate(text.maketrans('', '', vowels))
解决方案 3:
引用文档:
注意:当循环修改序列时,有一个微妙之处(这只能发生在可变序列,即列表上)。内部计数器用于跟踪下一个要使用的项目,并且每次迭代时都会递增。当此计数器达到序列的长度时,循环终止。这意味着,如果套件从序列中删除当前(或前一个)项目,则将跳过下一个项目(因为它获取了已经处理过的当前项目的索引)。同样,如果套件在当前项目之前在序列中插入一个项目,则下次循环时将再次处理当前项目。这可能会导致严重的错误,可以通过使用整个序列的切片制作临时副本来避免,例如,
for x in a[:]:
if x < 0: a.remove(x)
使用 迭代列表的浅表副本[:]
。您在迭代列表时修改列表,这会导致遗漏一些字母。
循环for
跟踪索引,因此当您删除索引处的项目时i
,第 位置处的下一个项目i+1
将移动到当前索引(i
),因此在下一次迭代中您实际上会选择i+2
第 一个项目。
让我们举一个简单的例子:
>>> text = "whoops"
>>> textlist = list(text)
>>> textlist
['w', 'h', 'o', 'o', 'p', 's']
for char in textlist:
if char.lower() in 'aeiou':
textlist.remove(char)
迭代 1:索引 = 0。
char = 'W'
因为它位于索引 0 处。由于它不满足该条件,因此您需要注意。
迭代 2:索引 = 1。
char = 'h'
因为它位于索引 1。这里没有什么可做的了。
迭代 3:索引 = 2。
char = 'o'
因为它位于索引 2 处。由于此项满足条件,所以它将从列表中删除,并且其右侧的所有项将向左移动一位以填补空白。
现在textlist
变成:
0 1 2 3 4
`['w', 'h', 'o', 'p', 's']`
如您所见,另一个'o'
已移至索引 2,即当前索引,因此它将在下一次迭代中被跳过。因此,这就是某些项目在您的迭代中被跳过的原因。每当您删除一个项目时,下一个项目都会从迭代中跳过。
迭代 4:索引 = 3。
char = 'p'
就像索引 3 那样。
....
使固定:
遍历列表的浅拷贝来修复此问题:
for char in textlist[:]: #note the [:]
if char.lower() in 'aeiou':
textlist.remove(char)
其他选择:
列表理解:
str.join
使用和的一行代码list comprehension
:
vowels = 'aeiou'
text = "Hey look Words!"
return "".join([char for char in text if char.lower() not in vowels])
正则表达式:
>>> import re
>>> text = "Hey look Words!"
>>> re.sub('[aeiou]', '', text, flags=re.I)
'Hy lk Wrds!'
解决方案 4:
您正在修改迭代的数据。不要这样做。
''.join(x for x in textlist in x not in VOWELS)
解决方案 5:
text = "Hey look Words!"
print filter(lambda x: x not in "AaEeIiOoUu", text)
输出
Hy lk Wrds!
解决方案 6:
您正在迭代一个列表并同时从中删除元素。
首先,我需要确保你清楚地理解了char
在 中的作用for char in textlist: ...
。以我们到达字母 'l' 的情况为例。情况不是这样的:
['H', 'e', 'y', ' ', 'l', 'o', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
^
char
char
和列表中字母 'l' 的位置之间没有任何联系。如果你修改char
,列表不会被修改。情况更像是这样的:
['H', 'e', 'y', ' ', 'l', 'o', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
^
char = 'l'
请注意,我保留了^
符号。这是管理循环的代码for char in textlist: ...
用来跟踪其在循环中的位置的隐藏指针。每次进入循环体时,指针都会前进,并且指针引用的字母会复制到中char
。
当你有两个连续的元音时,就会出现问题。我会向你展示从到达“l”的那一刻开始会发生什么。请注意,我还将单词“look”改为“leap”,以更清楚地说明发生了什么:
前进指针到下一个字符 ('l') 并复制到char
['H', 'e', 'y', ' ', 'l', 'e', 'a', 'p', ' ', 'W', 'o', 'r', 'd', 's', '!']
-> ^
char = 'l'
char
('l') 不是元音,因此不执行任何操作
前进指针到下一个字符 ('e') 并复制到char
['H', 'e', 'y', ' ', 'l', 'e', 'a', 'p', ' ', 'W', 'o', 'r', 'd', 's', '!']
-> ^
char = 'e'
char
('e') 是元音,因此删除第一次出现的char
('e')
['H', 'e', 'y', ' ', 'l', 'e', 'a', 'p', ' ', 'W', 'o', 'r', 'd', 's', '!']
^
['H', 'e', 'y', ' ', 'l', 'a', 'p', ' ', 'W', 'o', 'r', 'd', 's', '!']
^
['H', 'e', 'y', ' ', 'l', <- 'a', 'p', ' ', 'W', 'o', 'r', 'd', 's', '!']
^
['H', 'e', 'y', ' ', 'l', 'a', 'p', ' ', 'W', 'o', 'r', 'd', 's', '!']
^
前进指针到下一个字符 ('p') 并复制到char
['H', 'e', 'y', ' ', 'l', 'a', 'p', ' ', 'W', 'o', 'r', 'd', 's', '!']
-> ^
char = 'p'
当你删除“e”时,“e”后面的所有字符都会向左移动一个位置,所以就像remove
指针前进了一样。结果是你跳过了“a”。
一般来说,你应该避免在迭代列表时修改列表。最好从头开始构建一个新列表,而 Python 的列表推导式是执行此操作的完美工具。例如
print ''.join([char for char in "Hey look Words" if char.lower() not in "aeiou"])
但是如果你还没有了解理解的话,最好的方法可能是:
text = "Hey look Words!"
def anti_vowel(text):
textlist = list(text)
new_textlist = []
for char in textlist:
if char.lower() not in 'aeiou':
new_textlist.append(char)
return "".join(new_textlist)
print anti_vowel(text)
解决方案 7:
列表推导:
vowels = 'aeiou'
text = 'Hey look Words!'
result = [char for char in text if char not in vowels]
print ''.join(result)
解决方案 8:
其他人已经用你的代码解释了这个问题。对于你的任务,生成器表达式更简单,也不容易出错。
>>> text = "Hey look Words!"
>>> ''.join(c for c in text if c.lower() not in 'aeiou')
'Hy lk Wrds!'
或者
>>> ''.join(c for c in text if c not in 'AaEeIiOoUu')
'Hy lk Wrds!'
但这str.translate
是最好的方法。
解决方案 9:
您不应该从迭代列表中删除项目:但您可以使用列表理解语法从旧列表创建新列表。列表理解在这种情况下非常有用。您可以在此处阅读有关列表理解的内容
因此你的解决方案将是这样的:
text = "Hey look Words!"
def anti_vowel(text):
return "".join([char for char in list(text) if char.lower() not in 'aeiou'])
print anti_vowel(text)
很漂亮不是吗 :P
解决方案 10:
尽量不要对字符串使用 list() 函数。这会使事情变得更加复杂。
与 Java 不同,在 Python 中,字符串被视为数组。然后,尝试使用索引进行循环和 del 关键字。
for x in range(len(string)):
if string[x].lower() in "aeiou":
del string[x]
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 项目管理必备:盘点2024年13款好用的项目管理软件