在这段代码中我试图创建一个函数anti_vowel,它将从字符串中删除所有元音(aeiouAEIOU)。我认为它应该正常工作,但是当我运行它时,示例文本“嘿看起来是单词!”以“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)
答案 0 :(得分:153)
您正在修改您正在迭代的列表,这必然会导致一些不直观的行为。相反,请复制列表,这样就不会从正在迭代的内容中删除元素。
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!
那是怎么回事? Python中的漂亮for x in y
循环实际上只是语法糖:它仍然通过索引访问列表元素。因此,当您在迭代它时从列表中删除元素时,您开始跳过值(如上所示)。因此,您永远不会在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')
答案 1 :(得分:66)
其他答案告诉您为什么for
会在您更改列表时跳过项目。这个答案告诉你如何在没有显式循环的情况下删除字符串中的字符。
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()
static method生成该映射:
vowels = 'aeiou'
vowels += vowels.upper()
text.translate(text.maketrans('', '', vowels))
答案 2 :(得分:31)
注意:当序列被修改时,有一个微妙的 循环(这只能发生在可变序列,即列表中)。一个 内部计数器用于跟踪下一个使用的项目,以及 这在每次迭代时递增。当这个计数器到达时 循环终止的序列长度。这意味着,如果 suite从序列中删除当前(或前一个)项目 将跳过下一个项目(因为它获取当前项目的索引 已经治疗了)。同样,如果套件插入一个 当前项目之前的序列中的项目,当前项目将是 下次循环再次治疗。这可能导致讨厌 使用切片制作临时副本可以避免的错误 整个序列,例如,
for x in a[:]:
if x < 0: a.remove(x)
使用[:]
迭代列表的浅表副本。你在迭代它时修改一个列表,这将导致一些字母丢失。
for
循环跟踪索引,因此当您删除索引i
处的项目时,i+1
位置的下一个项目将转移到当前索引({{1} })因此在下一次迭代中你实际上会选择i
项。
让我们举一个简单的例子:
i+2
迭代1:索引= 0。
>>> text = "whoops"
>>> textlist = list(text)
>>> textlist
['w', 'h', 'o', 'o', 'p', 's']
for char in textlist:
if char.lower() in 'aeiou':
textlist.remove(char)
因为它在索引0处。因为它不满足那个条件你会注意到。
迭代2:索引= 1。
char = 'W'
因为它在索引1处。这里没什么可做的。
迭代3:索引= 2。
char = 'h'
因为它在索引2处。因为这个项目满足条件所以它将被从列表中删除,并且它右边的所有项目将向左移动一个位置以填补空白。 / p>
现在char = 'o'
变为:
textlist
正如您所见,另一个 0 1 2 3 4
`['w', 'h', 'o', 'p', 's']`
移动到索引2,即当前索引,因此在下一次迭代中将跳过它。因此,这就是在迭代中跳过某些项目的原因。每当你删除一个项目时,都会从迭代中跳过下一个项目。
迭代4:索引= 3。
'o'
因为它在索引3处。
...
迭代列表的浅层副本以解决此问题:
char = 'p'
列表理解:
使用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])
答案 3 :(得分:16)
您正在修改您正在迭代的数据。不要那样做。
''.join(x for x in textlist in x not in VOWELS)
答案 4 :(得分:8)
text = "Hey look Words!"
print filter(lambda x: x not in "AaEeIiOoUu", text)
<强>输出强>
Hy lk Wrds!
答案 5 :(得分: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的列表推导是完成这个的完美工具。 E.g。
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)
答案 6 :(得分:4)
vowels = 'aeiou'
text = 'Hey look Words!'
result = [char for char in text if char not in vowels]
print ''.join(result)
答案 7 :(得分:3)
其他人已经用你的代码解释了这个问题。对于您的任务,生成器表达式更容易,更不容易出错。
>>> 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
是最好的方式。
答案 8 :(得分:0)
您不应该从迭代的列表中删除项目: 但是您可以使用列表推导语法从旧列表中创建新列表。列表理解在这种情况下非常有用。您可以阅读有关列表理解here
的信息所以你的解决方案看起来像这样:
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
答案 9 :(得分:0)
尝试不对字符串使用list()函数。这将使事情变得更加复杂。
与Java不同,在Python中,字符串被视为数组。然后,尝试使用loop和del关键字的索引。
for x in range(len(string)):
if string[x].lower() in "aeiou":
del string[x]