为什么Python中的“if-letter in-dict”工作错误?

2024-09-28 23:38:12 发布

您现在位置:Python中文网/ 问答频道 /正文

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

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' 

我做错了什么


Tags: the函数字符串infordef字母list
3条回答

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循环中

原因是您在遍历列表时正在修改它-不应该这样做。 我将在这里分享@AlexMartelli在one of his answers中给出的建议:

Never alter the container you're looping on, because iterators on that container are not going to be informed of your alterations and, as you've noticed, that's quite likely to produce a very different loop and/or an incorrect one.

这里有一个替代解决方案(也使用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)

相关问题 更多 >