我想对所有字典值(在这种情况下是集合)进行并集。如果输入列表中只有两个字典,我只会得到预期的结果。
输入列表中的两个词典产生预期的结果:
>>> reduce((lambda x, y: x['a'] | y['a']), [{'a': {1, 2}}, {'a': {3, 4}}])
set([1, 2, 3, 4])
输入列表中的三个词典会产生TypeError。
预期结果:set([1, 2, 3, 4, 5, 6])
>>> reduce((lambda x, y: x['a'] | y['a']), [{'a': {1, 2}}, {'a': {3, 4}}, {'a': {5, 6}}])
Traceback (most recent call last):
File "<input>", line 1, in <module>
reduce((lambda x, y: x['a'] | y['a']), [{'a': {1, 2}}, {'a': {3, 4}}, {'a': {5, 6}}])
File "<input>", line 1, in <lambda>
reduce((lambda x, y: x['a'] | y['a']), [{'a': {1, 2}}, {'a': {3, 4}}, {'a': {5, 6}}])
TypeError: 'set' object has no attribute '__getitem__'
输入列表中的一个字典会生成一个字典,而不是一组字典。
预期结果:set([1, 2])
>>> reduce((lambda x, y: x['a'] | y['a']), [{'a': {1, 2}}])
{'a': set([1, 2])}
空的输入列表也会产生不同的TypeError。
预期结果:set([])
>>> reduce((lambda x, y: x['a'] | y['a']), [])
Traceback (most recent call last):
File "<input>", line 1, in <module>
reduce((lambda x, y: x['a'] | y['a']), [])
TypeError: reduce() of empty sequence with no initial value
我需要帮助来了解我在做什么错以及为什么会产生这些结果。
答案 0 :(得分:2)
TLDR:
reduce(function, iterable)
调用将function
递归应用于iterable
和先前结果的元素。这意味着function
的返回类型必须是有效的输入类型!
function
期望dict
,但产生set
。由于无法在x['y']
上调用set
,因此引发了TypeError
。iterable
只有两个元素时,function
仅一次应用,并且仅应用于这些元素。因此,永远不会遇到function
返回类型不是有效输入类型的问题。您必须先从map
dict
到set
,然后然后 reduce
set
个。
reduce(lambda x, y: x | y, map(lambda x: x['a'], [{'a': {1, 2}}, {'a': {3, 4}}, {'a': {5, 6}}]))
# merge via reduce ^ convert via map ^
reduce
在某些情况下会失败调用reduce(function, iterable)
等效于以下代码:
def reduce(function, iterable, start=None):
result = next(iterable) if start is None else start # 1.
for element in iterable:
result = function(result, element) # 2.
return result
这导致了几种情况:
iterable
具有一个元素,并且未设置start
result
是iterable
(1.
)的第一个元素
function
从未被调用;其返回和输入类型无关紧要iterable
具有两个元素,并且未设置start
result
是iterable
(1.
)function
元素(next
)上调用2.
function
永远不会收到自己的结果;其返回类型毫无意义iterable
具有两个以上的元素,并且未设置start
result
是iterable
(1.
)的第一个元素 function
元素(next
)上调用2.
function
元素(next
上调用2.
function
收到自己的结果;其返回类型和输入类型必须匹配iterable
为空或不为空,并且已设置start
start
是iterable
的第一个元素,则与上述相同iterable
为空并且未设置start
result
无法设置并且引发TypeError
(1.
)在您的情况下,即:
您的reduce
实际上一次在做两件事:分别转换/提取每个 元素,然后合并两个结果。这是经典的map / reduce任务:每个元素一个,所有元素一个。
您可以使用map
和reduce
内置函数将其直接分为两个单独的操作:
sets = map(lambda x: x['a'], [{'a': {1, 2}}, {'a': {3, 4}}, {'a': {5, 6}}])
result = reduce(lambda x, y: x | y, sets)
当然,您也可以直接嵌套这两个表达式。
map
部分可以使用理解表达。
sets = (x['a'] for x in [{'a': {1, 2}}, {'a': {3, 4}}, {'a': {5, 6}}])
result = reduce(lambda x, y: x | y, sets)
在Python3.8中,您也可以使用赋值表达式代替reduce
。
result = set()
result = [(result := (result | x['a'])) for x in [{'a': {1, 2}}, {'a': {3, 4}}, {'a': {5, 6}}]]
就知道了,把它写出来。
result = set()
for element in [{'a': {1, 2}}, {'a': {3, 4}}, {'a': {5, 6}}]:
result |= element['a']
答案 1 :(得分:1)
传递给reduce
的函数的输出必须与迭代器中的项具有相同的类型,以便它可以继续使用相同的函数聚合项值。
在您的情况下,lambda x, y: x['a'] | y['a']
的输出是一组{1, 2, 3, 4}
,因此,当reduce
尝试将第三项{'a': {5, 6}}
与{1, 2, 3, 4}
进行汇总时,它将之所以失败,是因为lambda函数将x
和y
都当作字典,并尝试通过键'a'
获取每个项,而键TypeError: reduce() of empty sequence with no initial value
却没有集合。
对于reduce
异常,您只需要向{}
提供一个初始值作为第三个参数,在您的情况下,它应该是一个空集:after
,但是您只需首先需要放弃将字典列表传递给它的想法,而是将集合列表传递给它的想法。
答案 2 :(得分:1)
find('div')
反复工作,它将在序列项之间应用归约聚合。例如,给定元素reduce
,i
和j
以及函数k
,它将处理foo
。
在您的示例中,foo(foo(i, j), k)
可以正常工作,给出一个foo(i, j)
,但是外部调用失败,因为结果是set
,没有键set
。后台中的语法'a'
调用[]
,这就是为什么您看到与此方法有关的错误的原因。
您能做什么?
一个小技巧是让您的函数输出字典,然后直接访问它的唯一值。这样可以确保您的函数始终输出带有键__getitem__
的字典。
'a'
更具可读性,您可以定义一个命名函数:
reduce((lambda x, y: {'a': x['a'] | y['a']}),
[{'a': {1, 2}}, {'a': {3, 4}}, {'a': {5, 6}}])['a']
# {1, 2, 3, 4, 5, 6}