为什么"if letter in dict"在 Python 中工作错误?



我正在尝试编写一个函数,该函数返回一个不在字母中的字符串字母猜测

words = list('abcdefghijklmnopqrstuvwxyz')
def getAvailableLetters(lettersGuessed):
[words.remove(letter) for letter in words if letter in lettersGuessed]
return ''.join([str(elem) for elem in words]) 

然后我调用我的函数

getAvailableLetters(['e', 's', 'i', 'k', 'p', 'r'])

但结果与我的预期不同。 我的输出:

'abcdfghjlmnoqstuvwxyz' 
# the letter 's' in the output, but it shouldn't be there.

正确输出:

'abcdfghjlmnoqtuvwxyz' 

我做错了什么?

原因是您在迭代列表时正在修改列表 - 不应该这样做。 我将在这里分享@AlexMartelli在他的一个回答中给出的建议:

永远不要更改您正在循环的容器,

因为该容器上的迭代器不会被告知您的更改,并且正如您已经注意到的那样,这很可能会产生非常不同的循环和/或不正确的循环。

这是一个替代解决方案(也使用 snake_case(,使用集合 - 假设可用的字母和猜测的字母都不会包含重复项(除非错误(:

all_letters = set('abcdefghijklmnopqrstuvwxyz')
def get_available_letters(letters_guessed):
available_letters = all_letters - set(letters_guessed)
return ''.join([str(letter) for letter in available_letters])

这里需要注意的是,上述解决方案可能不尊重您指定字母的顺序。

如果您仍然想坚持列表,请分两个阶段进行:

all_letters = list('abcdefghijklmnopqrstuvwxyz')
def get_available_letters(letters_guessed):
available_letters = [letter for letter in all_letters if letter not in letters_guessed]
return ''.join([str(letter) for letter in available_letters])

我将给你一个小例子,看看当你在迭代时从列表中删除元素时会发生什么:

l = ['a', 'b', 'c', 'd', 'e', 'f']
for i, c in enumerate(l):
print('_' * 25)
print('iteration', i)
print('index value', i)
print('elemnt at index ', i, ':', l[i])
print('list length:', len(l))
l.remove(c)
print('nafter removing an element')
print('list length:', len(l))
print('index value', i)
if len(l) > i:
print('elemnt at index ', i, ':', l[i]) # this element will not be removed
print('_' * 40)
print('list after iterateion:', l)

输出:

_________________________
iteration 0
index value 0
elemnt at index  0 : a
list length: 6
after removing an element
list length: 5
index value 0
elemnt at index  0 : b
_________________________
iteration 1
index value 1
elemnt at index  1 : c
list length: 5
after removing an element
list length: 4
index value 1
elemnt at index  1 : d
_________________________
iteration 2
index value 2
elemnt at index  2 : e
list length: 4
after removing an element
list length: 3
index value 2
elemnt at index  2 : f
________________________________________
list after iterateion: ['b', 'd', 'f']

如您所见,如果您在迭代列表时删除一个元素,则会修改列表的大小,并且从一个迭代到另一个迭代,您正在跳转下一个元素,for 循环在每次迭代后都会增加一个索引,期望抓取下一个元素,但您使用一个元素缩小列表,因此 for 循环实际上是跳转 1 个元素

如果要修改全局变量words可以使用:

def getAvailableLetters(lettersGuessed):
global words
words = [c for c in words if c not in lettersGuessed]
return ''.join([str(elem) for elem in words])
getAvailableLetters(['e', 's', 'i', 'k', 'p', 'r'])

输出:

'abcdfghjlmnoqtuvwxyz'

如果您只想返回不在lettersGuessed中的字母而不修改全局变量words

def getAvailableLetters(lettersGuessed):
return ''.join(c for c in words if c not in lettersGuessed)

remove()函数正在减少words的长度,而在Python中,循环变量仅在循环开始时初始化其限制。尝试用一个小例子来理解这一点

lis = [1, 2, 4, 6, 5]
for i in lis:
if i % 2 == 0:
lis.remove(i)

此代码输出[1, 4, 5],而预期的输出为[1, 5]。这是因为,i已经初始化为从 0 到 5 索引。第一次lis.remove()删除 2 时,lis 的长度减少了,它更新为lis = [1, 4, 6, 5],现在我应该指向 4(下一个元素(,而我现在指向 6,即索引 2(因为 2 在索引 1 处(,因此从不检查值 4。代码中也发生了同样的情况。

对于解决方案,最好使用while循环。此问题不会在 while 循环中出现,而只会在 for 循环中出现。

最新更新