Par*_*dox 77 python string list
在这段代码中,我试图创建一个函数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)
Run Code Online (Sandbox Code Playgroud)
Hen*_*ter 153
您正在修改正在迭代的列表,这必然会导致一些不直观的行为.相反,请复制列表,这样就不会从正在迭代的内容中删除元素.
for char in textlist[:]: #shallow copy of the list
# etc
Run Code Online (Sandbox Code Playgroud)
为了澄清您所看到的行为,请查看此信息.放在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!
Run Code Online (Sandbox Code Playgroud)
发生什么了?for x in y
Python中的好循环实际上只是语法糖:它仍然通过索引访问列表元素.因此,当您在迭代它时从列表中删除元素时,您开始跳过值(如上所示).其结果是,你永远看不到第二个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')
Run Code Online (Sandbox Code Playgroud)
Mar*_*ers 66
其他答案告诉您为什么for
在更改列表时跳过项目.这个答案告诉你如何在没有显式循环的情况下删除字符串中的字符.
vowels = 'aeiou'
vowels += vowels.upper()
text.translate(None, vowels)
Run Code Online (Sandbox Code Playgroud)
这将删除第二个参数中列出的所有字符.
演示:
>>> 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'
Run Code Online (Sandbox Code Playgroud)
在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)
Run Code Online (Sandbox Code Playgroud)
您还可以使用str.maketrans()
静态方法生成该映射:
vowels = 'aeiou'
vowels += vowels.upper()
text.translate(text.maketrans('', '', vowels))
Run Code Online (Sandbox Code Playgroud)
Ash*_*ary 31
引用文档:
注意:当循环修改序列时有一个微妙的变化(这只能发生在可变序列,即列表中).内部计数器用于跟踪下一个使用的项目,并在每次迭代时递增.当该计数器达到序列的长度时,循环终止.这意味着如果套件从序列中删除当前(或前一个)项目,则将跳过下一个项目(因为它获取已经处理的当前项目的索引).同样,如果套件在当前项目之前的序列中插入项目,则下次循环时将再次处理当前项目.这可能导致令人讨厌的错误,可以通过使用整个序列的切片进行临时复制来避免,例如,
for x in a[:]:
if x < 0: a.remove(x)
Run Code Online (Sandbox Code Playgroud)
使用重复列表的浅表副本[:]
.你在迭代它时修改一个列表,这将导致一些字母被遗漏.
该for
循环跟踪指数的,所以当你在索引中删除的项i
,在下一项目i+1
位置i转移到当前指数(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)
Run Code Online (Sandbox Code Playgroud)
迭代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']`
Run Code Online (Sandbox Code Playgroud)
正如您所见,另一个'o'
移动到索引2,即当前索引,因此在下一次迭代中将跳过它.因此,这就是在迭代中跳过某些项目的原因.每当您删除项目时,都会从迭代中跳过下一个项目.
迭代4:索引= 3.
char = 'p'
因为它在索引3处.
....
迭代列表的浅表副本以解决此问题:
for char in textlist[:]: #note the [:]
if char.lower() in 'aeiou':
textlist.remove(char)
Run Code Online (Sandbox Code Playgroud)
列表理解:
单线使用str.join
和a list comprehension
:
vowels = 'aeiou'
text = "Hey look Words!"
return "".join([char for char in text if char.lower() not in vowels])
Run Code Online (Sandbox Code Playgroud)
正则表达式:
>>> import re
>>> text = "Hey look Words!"
>>> re.sub('[aeiou]', '', text, flags=re.I)
'Hy lk Wrds!'
Run Code Online (Sandbox Code Playgroud)
Ign*_*ams 16
您正在修改您正在迭代的数据.不要那样做.
''.join(x for x in textlist in x not in VOWELS)
Run Code Online (Sandbox Code Playgroud)
text = "Hey look Words!"
print filter(lambda x: x not in "AaEeIiOoUu", text)
Run Code Online (Sandbox Code Playgroud)
产量
Hy lk Wrds!
Run Code Online (Sandbox Code Playgroud)
您正在迭代列表并同时从中删除元素.
首先,我需要确保您清楚了解的作用char
在for char in textlist: ...
.以我们达到字母"l"的情况为例.情况不是这样的:
['H', 'e', 'y', ' ', 'l', 'o', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
^
char
Run Code Online (Sandbox Code Playgroud)
char
列表中字母"l"的位置和位置之间没有任何关联.如果修改char
,则不会修改列表.情况更像这样:
['H', 'e', 'y', ' ', 'l', 'o', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
^
char = 'l'
Run Code Online (Sandbox Code Playgroud)
请注意,我保留了^
符号.这是管理for char in textlist: ...
循环的代码用于跟踪其在循环中的位置的隐藏指针.每次进入循环体时,指针都会前进,指针所引用的字母将被复制到char
.
当你连续有两个元音时就会出现问题.我会告诉你从达到'l'的那一刻发生了什么.请注意,我还将"look"更改为"leap",以便更清楚地了解正在发生的事情:
将指针前进到下一个字符('l')并复制到 char
['H', 'e', 'y', ' ', 'l', 'e', 'a', 'p', ' ', 'W', 'o', 'r', 'd', 's', '!']
-> ^
char = 'l'
Run Code Online (Sandbox Code Playgroud)
char
('l')不是元音,所以什么都不做
将指针前进到下一个字符('e')并复制到 char
['H', 'e', 'y', ' ', 'l', 'e', 'a', 'p', ' ', 'W', 'o', 'r', 'd', 's', '!']
-> ^
char = 'e'
Run Code Online (Sandbox Code Playgroud)
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', '!']
^
Run Code Online (Sandbox Code Playgroud)
将指针前进到下一个字符('p')并复制到 char
['H', 'e', 'y', ' ', 'l', 'a', 'p', ' ', 'W', 'o', 'r', 'd', 's', '!']
-> ^
char = 'p'
Run Code Online (Sandbox Code Playgroud)
当你删除'e'后,'e'后面的所有字符都向左移动了一个地方,所以好像remove
已经推进了指针.结果是你跳过'a'.
通常,您应该避免在迭代时修改列表.最好从头开始构建一个新列表,而Python的列表推导是完成此任务的完美工具.例如
print ''.join([char for char in "Hey look Words" if char.lower() not in "aeiou"])
Run Code Online (Sandbox Code Playgroud)
但如果你还没有学过理解,最好的方法可能是:
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)
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
7039 次 |
最近记录: |