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}]
现在问题:
- 在函数dedup中,过滤后的结果好像保存在set中,请问item从list到set的时候,list的顺序是如何保持的?
- 函数中没有使用明确的return语句,是否意味着集合“seen”被隐式returned?
- 你能解释一下测试中的两个 lambda 定义吗?从结果来看,前者好像是检查了dict类型的k和v,而后者只检查了dict的key是否有重复。
您并未实际将列表转换为 set
,请注意 set
仅用于跟踪已看到的项目。否则,你只是线性迭代,保持原来的顺序
不,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]
- 在第一个示例中,您过滤成对的项目,您不关心键,只关心值,所以将它们存储在一个元组中:
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
我是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}]
现在问题:
- 在函数dedup中,过滤后的结果好像保存在set中,请问item从list到set的时候,list的顺序是如何保持的?
- 函数中没有使用明确的return语句,是否意味着集合“seen”被隐式returned?
- 你能解释一下测试中的两个 lambda 定义吗?从结果来看,前者好像是检查了dict类型的k和v,而后者只检查了dict的key是否有重复。
您并未实际将列表转换为
set
,请注意set
仅用于跟踪已看到的项目。否则,你只是线性迭代,保持原来的顺序不,
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]
- 在第一个示例中,您过滤成对的项目,您不关心键,只关心值,所以将它们存储在一个元组中:
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