functools.reduce 在 Python 中没有按预期工作
functools.reduce in Python not working as expected
我想使用 functools.reduce
函数
对嵌套在列表中的字典的键求和
我可以在没有 functools.reduce
功能的情况下使用以下简单程序完成此操作:
dict1 = {'a': '1', 'b': '2'}
dict2 = {'a': '5', 'b': '0'}
dict3 = {'a': '7', 'b': '3'}
data_list = [dict1, dict2, dict3]
total_a = 0
total_b = 0
for record in data_list:
total_a += eval(record['a'])
total_b += eval(record['b'])
print(total_a)
print(total_b)
然而正如我所说,我想使用 functools.reduce
方法来产生相同的结果。
这是我尝试将 functools.reduce
与 lambda 表达式结合使用的尝试:
from functools import reduce
dict1 = {'a': '1', 'b': '2'}
dict2 = {'a': '5', 'b': '0'}
dict3 = {'a': '7', 'b': '3'}
data_list = [dict1, dict2, dict3]
total_a = reduce(lambda x, y: int(x['a']) + int(y['a']),data_list)
total_b = reduce(lambda x, y: int(x['b']) + int(y['b']),data_list )
print(total_a)
print(total_b)
不幸的是,我收到以下错误,不知道为什么:
TypeError: 'int' object is not subscriptable
有人知道我为什么会收到此错误吗?
您误解了 reduce()
的工作原理。在传递给它的函数中,它的第一个参数是 so 的部分结果,与传递给 reduce()
的 iterable 没有直接关系。可迭代对象一次传递一个元素,传递给函数的第二个参数。既然要求和,那么“部分结果”的初值需要为0,同样需要传给reduce()
.
因此,总而言之,这些行将打印您想要的内容:
print(reduce(lambda x, y: x + int(y['a']), data_list, 0))
print(reduce(lambda x, y: x + int(y['b']), data_list, 0))
编辑:将 eval()
替换为上面的 int()
,因此它与编辑后的问题匹配。不过,这与答案无关。
编辑 2:您不断更改问题,但我不会继续更改答案以匹配 ;-) 上面的代码完全回答了问题的早期版本,没有任何内容 material 已更改。完全相同的事情仍在起作用,需要完全相同的方法。
类型上的光泽
虽然 Python 不需要明确的类型声明,但有时它们会有所帮助。
如果你有一个A
类型的可迭代交付对象,并且reduce()
的结果是B
类型,那么传递给[=]的第一个参数的签名12=] 必须是
def reduction_function(x: B, y: A) -> B
在示例中,A
是 dict
,B
是 int
。为两者都传递命令是不可能的。这就是为什么我们需要在这种情况下指定类型 B
(int
) 的初始值。
在文档示例中,A
和 B
通常是 两者 int
或 float
。那么对于 reduce()
的第一个参数,一个简单的 + 或 * 已经是 type-correct。
TypeError: 'int' object is not subscriptable
Does someone know why I am getting this error?
首先,让我们将(双关语)样本减少到最少:
>>> from functools import reduce
>>> data = [{"a": 1}, {"a": 2}, {"a": 3}]
>>> reduce(lambda x, y: x["a"] + y["a"], data)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <lambda>
TypeError: 'int' object is not subscriptable
同样的错误。但请注意:
>>> reduce(lambda x, y: x["a"] + y["a"], data[:2])
3
行得通。发生什么了?为了简化,让我们将 lambda 表达式分配给一个变量:
f = lambda x, y: x['a'] + y['a']
Reduce 像这样组合输入:
# Example: reduce(lambda x, y: x["a"] + y["a"], data[:2])
>>> f(data[0], data[1])
# which evaluates in steps like this:
>>> data[0]["a"] + data[1]["a"]
>>> 1 + 2
>>> 3
但是减少完整列表时会发生什么?这评估为
# Example: reduce(lambda x, y: x["a"] + y["a"], data)
>>> f(f(data[0], data[1]), data[2])
# which evaluates in steps like this:
>>> f(data[0]["a"] + data[1]["a"], data[2])
>>> f(1 + 2, data[2])
>>> f(3, data[2])
>>> 3["a"] + data[2]["a"]
所以这个错误是因为它试图从整数 3.
访问项目 "a"
基本上:传递给 reduce 的函数的输出必须是可接受的,因为它是第一个参数。在您的示例中,lambda 期望字典作为其第一个参数,并且 returns 是一个整数。
缩减函数接收当前缩减值加上下一个要缩减的迭代项。诀窍在于选择减少值的样子。在您的情况下,如果您选择一个包含 'a' 和 'b' 的缩减值的 2 项列表,则缩减函数只会将下一个 'a' 和 'b' 添加到这些值.缩减最容易写成几条语句,因此应该从匿名 lambda 转移到常规函数。从 [0, 0]
的初始值设定项开始,以保存缩减的 'a' 和 'b',您将得到:
from functools import reduce
def reducer(accum, next_dict):
print(accum, next_dict) # debug trace
accum[0] += int(next_dict['a'])
accum[1] += int(next_dict['b'])
return accum
dict1 = {'a': '1', 'b': '2'}
dict2 = {'a': '5', 'b': '0'}
dict3 = {'a': '7', 'b': '3'}
data_list = [dict1, dict2, dict3]
total_a, total_b = reduce(reducer, data_list, [0, 0])
由于我们要添加整数,reduce
的替代方法是 sum
:
data_list = [{'a': '1', 'b': '2'}, {'a': '5', 'b': '0'}, {'a': '7', 'b': '3'}]
total_a, total_b = map(sum, zip(*((int(d['a']),int(d['b'])) for d in data_list)))
print(total_a, total_b)
# 13 5
我想使用 functools.reduce
函数
我可以在没有 functools.reduce
功能的情况下使用以下简单程序完成此操作:
dict1 = {'a': '1', 'b': '2'}
dict2 = {'a': '5', 'b': '0'}
dict3 = {'a': '7', 'b': '3'}
data_list = [dict1, dict2, dict3]
total_a = 0
total_b = 0
for record in data_list:
total_a += eval(record['a'])
total_b += eval(record['b'])
print(total_a)
print(total_b)
然而正如我所说,我想使用 functools.reduce
方法来产生相同的结果。
这是我尝试将 functools.reduce
与 lambda 表达式结合使用的尝试:
from functools import reduce
dict1 = {'a': '1', 'b': '2'}
dict2 = {'a': '5', 'b': '0'}
dict3 = {'a': '7', 'b': '3'}
data_list = [dict1, dict2, dict3]
total_a = reduce(lambda x, y: int(x['a']) + int(y['a']),data_list)
total_b = reduce(lambda x, y: int(x['b']) + int(y['b']),data_list )
print(total_a)
print(total_b)
不幸的是,我收到以下错误,不知道为什么:
TypeError: 'int' object is not subscriptable
有人知道我为什么会收到此错误吗?
您误解了 reduce()
的工作原理。在传递给它的函数中,它的第一个参数是 so 的部分结果,与传递给 reduce()
的 iterable 没有直接关系。可迭代对象一次传递一个元素,传递给函数的第二个参数。既然要求和,那么“部分结果”的初值需要为0,同样需要传给reduce()
.
因此,总而言之,这些行将打印您想要的内容:
print(reduce(lambda x, y: x + int(y['a']), data_list, 0))
print(reduce(lambda x, y: x + int(y['b']), data_list, 0))
编辑:将 eval()
替换为上面的 int()
,因此它与编辑后的问题匹配。不过,这与答案无关。
编辑 2:您不断更改问题,但我不会继续更改答案以匹配 ;-) 上面的代码完全回答了问题的早期版本,没有任何内容 material 已更改。完全相同的事情仍在起作用,需要完全相同的方法。
类型上的光泽
虽然 Python 不需要明确的类型声明,但有时它们会有所帮助。
如果你有一个A
类型的可迭代交付对象,并且reduce()
的结果是B
类型,那么传递给[=]的第一个参数的签名12=] 必须是
def reduction_function(x: B, y: A) -> B
在示例中,A
是 dict
,B
是 int
。为两者都传递命令是不可能的。这就是为什么我们需要在这种情况下指定类型 B
(int
) 的初始值。
在文档示例中,A
和 B
通常是 两者 int
或 float
。那么对于 reduce()
的第一个参数,一个简单的 + 或 * 已经是 type-correct。
TypeError: 'int' object is not subscriptable
Does someone know why I am getting this error?
首先,让我们将(双关语)样本减少到最少:
>>> from functools import reduce
>>> data = [{"a": 1}, {"a": 2}, {"a": 3}]
>>> reduce(lambda x, y: x["a"] + y["a"], data)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <lambda>
TypeError: 'int' object is not subscriptable
同样的错误。但请注意:
>>> reduce(lambda x, y: x["a"] + y["a"], data[:2])
3
行得通。发生什么了?为了简化,让我们将 lambda 表达式分配给一个变量:
f = lambda x, y: x['a'] + y['a']
Reduce 像这样组合输入:
# Example: reduce(lambda x, y: x["a"] + y["a"], data[:2])
>>> f(data[0], data[1])
# which evaluates in steps like this:
>>> data[0]["a"] + data[1]["a"]
>>> 1 + 2
>>> 3
但是减少完整列表时会发生什么?这评估为
# Example: reduce(lambda x, y: x["a"] + y["a"], data)
>>> f(f(data[0], data[1]), data[2])
# which evaluates in steps like this:
>>> f(data[0]["a"] + data[1]["a"], data[2])
>>> f(1 + 2, data[2])
>>> f(3, data[2])
>>> 3["a"] + data[2]["a"]
所以这个错误是因为它试图从整数 3.
访问项目"a"
基本上:传递给 reduce 的函数的输出必须是可接受的,因为它是第一个参数。在您的示例中,lambda 期望字典作为其第一个参数,并且 returns 是一个整数。
缩减函数接收当前缩减值加上下一个要缩减的迭代项。诀窍在于选择减少值的样子。在您的情况下,如果您选择一个包含 'a' 和 'b' 的缩减值的 2 项列表,则缩减函数只会将下一个 'a' 和 'b' 添加到这些值.缩减最容易写成几条语句,因此应该从匿名 lambda 转移到常规函数。从 [0, 0]
的初始值设定项开始,以保存缩减的 'a' 和 'b',您将得到:
from functools import reduce
def reducer(accum, next_dict):
print(accum, next_dict) # debug trace
accum[0] += int(next_dict['a'])
accum[1] += int(next_dict['b'])
return accum
dict1 = {'a': '1', 'b': '2'}
dict2 = {'a': '5', 'b': '0'}
dict3 = {'a': '7', 'b': '3'}
data_list = [dict1, dict2, dict3]
total_a, total_b = reduce(reducer, data_list, [0, 0])
由于我们要添加整数,reduce
的替代方法是 sum
:
data_list = [{'a': '1', 'b': '2'}, {'a': '5', 'b': '0'}, {'a': '7', 'b': '3'}]
total_a, total_b = map(sum, zip(*((int(d['a']),int(d['b'])) for d in data_list)))
print(total_a, total_b)
# 13 5