【问题标题】:Why "if letter in dict" working wrong in Python?为什么“if letter in dict”在 Python 中工作错误?
【发布时间】:2020-05-27 17:00:55
【问题描述】:

我正在尝试编写一个函数,它返回一个字符串而不是 lettersGuessed

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' 

我做错了什么?

【问题讨论】:

  • 迭代时不要移除元素。这将导致意想不到的行为。您可以遍历副本并删除元素。即for letter in words[:]:words.remove(letter)。也不需要建立列表[words.remove(letter) for letter in words[:] if letter in lettersGuessed]remove 是就地操作,不会产生值。所以你实际上是在构建一个None 值列表。
  • 您在迭代列表的同时修改列表,这意味着您将跳过列表中的某些值。改用return ''.join(l for l in words if l not in lettersGuessed) 之类的方法

标签: python string list if-statement list-comprehension


【解决方案1】:

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

永远不要更改您正在循环的容器,因为该容器上的迭代器不会被告知您的更改,而且正如您所注意到的,这很可能会产生非常不同的循环和/或不正确的循环.

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

【讨论】:

    【解决方案2】:

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

    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)
    

    【讨论】:

      【解决方案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 循环中。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-16
        相关资源
        最近更新 更多