关于python:循环“忘记”删除一些项目

Loop “Forgets” to Remove Some Items

本问题已经有最佳答案,请猛点这里访问。

在这段代码中,我试图创建一个反元音函数,它将删除字符串中的所有元音(aeiouaeiou)。我认为它应该可以工作,但当我运行它时,示例文本"嘿,看单词!"返回为"hy-lk words!".它"忘记"删除最后一个"o"。怎么会这样?

1
2
3
4
5
6
7
8
9
10
11
12
13
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)


你的列表迭代修改过你,这是有时在一些unintuitive行为的结果。而不是创建一个复制列表,所以你不要删除元素从你再通过迭代。

1
2
for char in textlist[:]: #shallow copy of the list
    # etc

要澄清的行为,你看到的,看看这个。在你开始把print char, textlist(原创)环。你也许会认为,这是打印出来,与垂直的字符串,列表,但你会得到什么:这是真的

1
2
3
4
5
6
7
8
9
10
11
12
13
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只是语法糖:它是非常安静accesses列表元素的索引。所以,当你删除从列表元素而迭代过它,你开始跳过值(你可以看到上面的)。As a result,你看不到第二o跳过它,因为你"look"过;有先进的"过去"的指标,当你删除它以前的元素。然后,当你把你的o"Words"CRP中,删除第一'o'发生),这是一个你skipped之前。

如有上述列表理解他人,这可能是在(甚至更好的清洁方式,虽然这样做)。化妆品使用的事实,可迭代变量是Python的字符串:

1
2
def remove_vowels(text): # function names should start with verbs! :)
    return ''.join(ch for ch in text if ch.lower() not in 'aeiou')


其他答案告诉你为什么你的年龄forskips项目列表。这个答案告诉你你应该删除的字符在字符串中没有环,而不是。

用途:str.translate()

1
2
3
vowels = 'aeiou'
vowels += vowels.upper()
text.translate(None, vowels)

在删除所有字符列在第二的论点。

演示:

1
2
3
4
5
6
7
8
>>> 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(Pythonstr.translate()3,方法2:在unicode.translate())不同,它不需要一deletechars参数;第一个参数是一个字典序数(Unicode值映射到新的价值观而不是整数)。使用None任何字符,需要删除:

1
2
3
4
5
# Python 3 code
vowels = 'aeiou'
vowels += vowels.upper()
vowels_table = dict.fromkeys(map(ord, vowels))
text.translate(vowels_table)

所以,你可以使用静态方法str.maketrans()产生这样的映射:

1
2
3
vowels = 'aeiou'
vowels += vowels.upper()
text.translate(text.maketrans('', '', vowels))


引证:从文档

Note: There is a subtlety when the sequence is being modified by the
loop (this can only occur for mutable sequences, i.e. lists). An
internal counter is used to keep track of which item is used next, and
this is incremented on each iteration. When this counter has reached
the length of the sequence the loop terminates. This means that if the
suite deletes the current (or a previous) item from the sequence, the
next item will be skipped (since it gets the index of the current item
which has already been treated). Likewise, if the suite inserts an
item in the sequence before the current item, the current item will be
treated again the next time through the loop. This can lead to nasty
bugs that can be avoided by making a temporary copy using a slice of
the whole sequence, e.g.,

1
2
for x in a[:]:
    if x < 0: a.remove(x)

iterate over a浅表[:]复制使用。你是修饰a list while迭代过它,这将导致一些文学被错过了。

一个for履带环指标,所以当你在i删除项目在一项指标,在友好的位置,i+1th流指数(i)和在下一迭代,因此你真的i+2th拾取物品。

我们以简单的例子:

1
2
3
4
5
6
7
>>> 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变成:

1
2
   0    1    2    3    4
`['w', 'h', 'o', 'p', 's']`

你可以看到'o'搬到其他指标2,即目前的指标,所以它会在下一skipped迭代。因此,这是有原因的,是把skipped项目的迭代。当你在一个项目删除项目从skipped迭代。

迭代指数= 4:3。

它是在char = 'p'指数3。

…………………

修复:

iterate over a浅复制固定这个问题列表:

1
2
3
for char in textlist[:]:        #note the [:]
    if char.lower() in 'aeiou':
        textlist.remove(char)

其他的选择:

发展战略:

一个系统和一list comprehensionstr.join

1
2
3
vowels = 'aeiou'
text ="Hey look Words!"
return"".join([char for char in text if char.lower() not in vowels])

正则表达式:

1
2
3
4
>>> import re
>>> text ="Hey look Words!"
>>> re.sub('[aeiou]', '', text, flags=re.I)
'Hy lk Wrds!'


你是你修改过的数据的迭代。不要做。

1
''.join(x for x in textlist in x not in VOWELS)

1
2
3
text ="Hey look Words!"

print filter(lambda x: x not in"AaEeIiOoUu", text)

输出

1
Hy lk Wrds!

您正在迭代一个列表,同时从中删除元素。

首先,我需要确保您清楚地了解charfor char in textlist: ...中的作用。以我们到达字母"L"的位置为例。情况并非如此:

1
2
3
['H', 'e', 'y', ' ', 'l', 'o', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
                      ^
                    char

char与列表中字母"l"的位置之间没有联系。如果修改char,则不会修改列表。情况更像这样:

1
2
3
['H', 'e', 'y', ' ', 'l', 'o', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
                      ^
char = 'l'

注意,我保留了^符号。这是管理for char in textlist: ...循环的代码用来跟踪其在循环中的位置的隐藏指针。每次进入循环体时,指针都会前进,指针引用的字母会被复制到char中。

你的问题发生在你连续有两个元音的时候。我会告诉你从你到达"L"的地方发生了什么。请注意,我还将"look"改为"leap",以便更清楚地了解正在发生的事情:

将指针前进到下一个字符("l")并复制到EDOCX1[0]

1
2
3
['H', 'e', 'y', ' ', 'l', 'e', 'a', 'p', ' ', 'W', 'o', 'r', 'd', 's', '!']
                   -> ^
char = 'l'

char(‘l’)不是元音,所以什么都不做。

将指针前进到下一个字符("e")并复制到EDOCX1[0]

1
2
3
['H', 'e', 'y', ' ', 'l', 'e', 'a', 'p', ' ', 'W', 'o', 'r', 'd', 's', '!']
                        -> ^
char = 'e'

char(‘e’)是一个元音,因此删除char(‘e’)的第一个出现处。

1
2
3
4
5
6
7
8
9
10
11
['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")并复制到EDOCX1[0]

1
2
3
['H', 'e', 'y', ' ', 'l', 'a', 'p', ' ', 'W', 'o', 'r', 'd', 's', '!']
                             -> ^
char = 'p'

删除"e"后,所有"e"的字符都向左移动了一个位置,所以就好像remove已经提前了指针。结果是您跳过了"a"。

一般来说,在遍历列表时应避免修改列表。最好从头构建一个新的列表,而python的列表理解是实现这一点的完美工具。例如。

1
print ''.join([char for char in"Hey look Words" if char.lower() not in"aeiou"])

但如果你还没有学会理解,最好的方法可能是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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)


列表理解:

1
2
3
4
vowels = 'aeiou'
text = 'Hey look Words!'
result = [char for char in text if char not in vowels]
print ''.join(result)

其他人可能已经在你的代码的问题。你的任务是不容易的表达A发电机的误差之和。

1
2
3
>>> text ="Hey look Words!"
>>> ''.join(c for c in text if c.lower() not in 'aeiou')
'Hy lk Wrds!'

1
2
>>> ''.join(c for c in text if c not in 'AaEeIiOoUu')
'Hy lk Wrds!'

然而,str.translate是最好的路要走。


尽量不要在字符串上使用list()函数。这会使事情变得更加复杂。

与Java不同,在Python中,字符串被认为是数组。然后,尝试对loop和del关键字使用索引。

1
2
3
for x in range(len(string)):
    if string[x].lower() in"aeiou":
        del string[x]

您不应该从正在迭代的列表中删除项:但是,您可以使用列表理解语法从旧列表中创建新列表。在这种情况下,列表理解非常有用。你可以在这里阅读列表理解

因此,您的解决方案如下所示:

1
2
3
4
5
6
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