Python 为什么这个函数可以工作,因为for循环显然用参数中的值填充dict以允许比较?

Python 为什么这个函数可以工作,因为for循环显然用参数中的值填充dict以允许比较?,python,function,dictionary,for-loop,Python,Function,Dictionary,For Loop,下面是一个练习面试问题,下面是符合问题的正确答案。问题是我不知道这个函数是如何工作的。我在答案下面解释我的困惑 给定一个数组a,该数组只包含范围在1到之间的数字 a、 长度,找到第二个重复编号对应的第一个重复编号 事件具有最小的索引。换句话说,如果有更多 多于1个重复的数字,返回第二个重复的数字 事件的索引小于另一个事件的第二个事件 数字是。如果没有这样的元素,则返回-1 答复: def firstDuplicate(a): oldies={} notfound=True

下面是一个练习面试问题,下面是符合问题的正确答案。问题是我不知道这个函数是如何工作的。我在答案下面解释我的困惑

给定一个数组a,该数组只包含范围在1到之间的数字 a、 长度,找到第二个重复编号对应的第一个重复编号 事件具有最小的索引。换句话说,如果有更多 多于1个重复的数字,返回第二个重复的数字 事件的索引小于另一个事件的第二个事件 数字是。如果没有这样的元素,则返回-1

答复:

def firstDuplicate(a):
    oldies={}
    notfound=True
    for i in range(len(a)):
        try:
            if oldies[a[i]]==a[i]:
                notfound=False
                return a[i]     
        except:
            oldies[a[i]]=a[i]
    if notfound:
        return -1
所以这个函数创建了一个空的dict,oldies。但是在for循环中,我不明白这行是如何工作的:

if oldies[a[i]]==a[i]:
在我看来,==运算符将空dict“oldies”的索引值与如下列表中的参数进行比较,例如:

a = [2, 4, 1, 3, 4, 5, 1, 5, 7, 8, 2, 4,]

但显然,当进行这种比较时,“老歌”并不是空的。这是怎么回事?

我将分解这个答案,并解释其中的一些问题

解释 首先,该函数创建一个空的dict和一个布尔值。然后,它在从0到n的
范围内迭代,该范围表示输入列表的长度。从那里,它将
尝试
将列表中索引
i
处的值与字典中最初为空的值进行比较。由于字典中没有正确的键,它将抛出一个
KeyError
。这将被
except
语句捕获并添加到字典中。如果输入列表中的第一个值是
'a'
,那么dict现在看起来像
{'a':'a'}
。假设
'a'
稍后出现在列表中,它最终将捕获find并返回该值。如果未找到重复项,则返回
-1

问题
  • 迭代列表不需要迭代范围。直接迭代列表将不需要检查它的长度或创建
    范围
    对象,因此它的性能可能更高
  • 在开头创建布尔值并在结尾使用它是多余的,因为对
    return
    的任何调用都将立即退出该函数。因此,如果发现重复项,if块中的
    返回将退出,循环后不会调用任何内容
  • 使用字典是一种糟糕的结构选择。有更多的开销空间,因为它需要维护键、值及其关系。像一套,甚至是一张清单之类的东西会是一个更好的选择
  • 假设我们将
    oldies
    更改为一个集合,并直接在列表上迭代,则该代码中的整个条件块可以简化为一个简单的
    in
    语句。如上所述,通过正确使用return,这也消除了最终条件
  • 尽管我建议在这种情况下不要使用它,但except块应该 捕获显式异常,而不是常规捕获所有异常。除了KeyError:
  • 之外,应该使用类似于
    的内容
    最终结果将类似于:

    def firstDuplicate(a):
        oldies = set()
    
        for i in a:
            if i in oldies:
                return i
            else:
                oldies.add(i)
        return -1
    
    
    print(firstDuplicate(['a', 'b', 'c', 'b', 'a', 'c']))
    
    结果:
    b


    使用
    itertools
    或其他软件包可能会有一些更好的解决方案。

    旧版本仅在第一次迭代时为空。当某个键丢失时,它将被添加到
    块中,但
    块除外。这基本上是使用一个
    dict
    作为
    set
    ,键本身作为伪值。天哪,这个答案绝对可怕。你最好用它来理解它为什么有效,而不是从中学习如何做事。啊,我现在明白了。“除了”下面的这一行基本上是将a的值添加到“oldies”:oldies[a[i]]=a[i]我想知道他们是否有意使用这个更复杂的算法来剔除那些无法理解的候选者。@Barmar这实际上是数百个用户提交的解决方案中的一个答案,因此它不用于剔除候选者。我是从暗斗中得到的。com@TJE当你写“正确答案”时,我解释为这是你参加考试后提供的唯一答案。你本可以说这是一个正确的答案。@Barmar对此表示抱歉。我主要关心的是理解它为什么起作用,我没有意识到它有什么问题。因此,我从未想过我会让这里的人认为这是测试人员认为的“最佳”正确答案。