python 关于生成器和关键字 yield 的问题

python question regarding generator and keyword yield

我是Python的新手,所以请多多包涵。该片段来自 "Python cookbook" 一书,内容涉及如何从不可哈希的集合中删除重复项。

def dedup(items, key=None):
    seen = set()
    for item in items:
        val = item if key is None else key(item)
        if val not in seen:
            yield val
            seen.add(val)

if __name__ == '__main__':
    a = [{'x':1, 'y':2}, {'x':1, 'y':3}, {'x':1, 'y':2}, {'x':2, 'y':4}]
    print(list(dedup(a, key=lambda d:(d['x'],d['y']))))

测试时,根据"key"的定义方式,结果显示为:

a = [ {'x':1, 'y':2}, {'x':1, 'y':3}, {'x':1, 'y':2}, {'x':2, 'y':4}]
>>> list(dedupe(a, key=lambda d: (d['x'],d['y'])))
[{'x': 1, 'y': 2}, {'x': 1, 'y': 3}, {'x': 2, 'y': 4}]
>>> list(dedupe(a, key=lambda d: d['x']))
[{'x': 1, 'y': 2}, {'x': 2, 'y': 4}]

现在问题:

  1. 在函数dedup中,过滤后的结果好像保存在set中,请问item从list到set的时候,list的顺序是如何保持的?
  2. 函数中没有使用明确的return语句,是否意味着集合“seen”被隐式returned?
  3. 你能解释一下测试中的两个 lambda 定义吗?从结果来看,前者好像是检查了dict类型的k和v,而后者只检查了dict的key是否有重复。
  1. 您并未实际将列表转换为 set,请注意 set 仅用于跟踪已看到的项目。否则,你只是线性迭代,保持原来的顺序

  2. 不,seen 永远不会返回。我会研究生成器,但要点是 yield 将函数转换为生成器,一次为您生成一个值。 list 消耗发电机。 seen 将留在函数的范围内,直到生成器完全消耗(for 循环退出)

def f():
    seen = set()
    for i in range(3):
        seen.add(i)
        print(seen)
        yield i

list(f())
{0}
{0, 1}
{0, 1, 2}
[0, 1, 2]
  1. 在第一个示例中,您过滤成对的项目,您不关心键,只关心值,所以将它们存储在一个元组中:
a = set()
d = {'x': 1, 'y': 2}
a.add((d['y'], d['y']))

在第二个示例中,您只关心 x 值,因此您可以通过调用 seen.add(d['x']).

显式添加它

您可以在 map 中测试您的 lambda 函数以查看它们产生的结果:

a = [{'x':1, 'y':2}, {'x':1, 'y':3}, {'x':1, 'y':2}, {'x':2, 'y':4}]

# first
list(map(lambda d: (d['x'], d['y']), a))
[(1, 2), (1, 3), (1, 2), (2, 4)]

# second
list(map(lambda d: d['x'], a))
[1, 1, 1, 2]

或在 for 循环中:

# this is bad practice, but it illustrates the point
x = lambda d: d['x']
for val in a:
    print(x(val))
1
1
1
2