The built-in functools.reduce function (implemented in _functools module) does not accept keyword arguments for any of its parameters (function, iterable, initializer). This behavior is inconsistent with other functions in the functools module (like partial, lru_cache, etc.) and limits its usability in functional programming patterns, especially with functools.partial.
Steps to Reproduce
Import reduce and partial from functools, and import an operator (e.g., operator.mul).
Attempt to create a partial function for reduction using keyword arguments.
Observe the TypeError.
Code Example:
fromfunctoolsimportreduce, partialimportoperatorresult1=reduce(operator.mul, [2, 3, 5], 1) # 30, without problem# Attempt to create a product reducer with initializer=1 using partialproduct_int=partial(reduce, operator.mul, initial=1)
result2=product_int([2, 3, 5]) # Expected: 30
Expected Behavior
The reduce function should accept keyword arguments for its parameters (at least for initializer), allowing constructions like:
This would align with the flexibility offered by other Python functions and improve compatibility with functools.partial.
Actual Behavior
A TypeError is raised:
TypeError: reduce() takes no keyword arguments
Environment
Python Version: CPython 3.12.7
Operating System: Windows 10
Note: The issue stems from the C implementation of reduce in _functools, which does not support keyword arguments. The pure-Python fallback implementation in functools.py does handle keyword arguments but is overridden by the C version when available.
Root Cause Analysis
The C implementation of reduce (in _functools module) is optimized for performance and does not define a signature that accepts keyword arguments. This is a common optimization in C-extensions but here it limits functionality. The pure-Python version in functools.py (used as fallback if _functools is not available) does support keyword arguments, as seen in its source:
defreduce(function, sequence, initial=_initial_missing):
# ... (implementation that can handle keyword arguments)
However, since the C implementation is almost always present (as _functools is a built-in module), the pure-Python version is not used.
Possible Solutions
Modify the C implementation to support keyword arguments for its parameters, especially initial. This would involve using a calling convention that supports keywords (e.g., METH_FASTCALL | METH_KEYWORDS) and updating the argument parsing logic to use PyArg_ParseTupleAndKeywords (or equivalent), while maintaining full backward compatibility with existing code that uses positional arguments.
Make the initial parameter also a keyword argument in the C version. This would be a simpler change and directly enable the desired use case with partial.
Ensure consistency between the C and pure-Python implementations. The pure-Python version should be the reference for behavior.
Additional Context
This limitation prevents elegant functional composition using partial, which is a common paradigm in functional programming.
The workaround is to use a lambda or helper function, which is less readable and less efficient:
The issue is similar to historical problems in Cython-compiled functions (see Cython issue #2881), where functions by default did not accept keyword arguments for performance reasons. This was solvable via the always_allow_keywords directive.
Other functions in functools (like partial, lru_cache) extensively use and support keyword arguments, making reduce an outlier.
Linked PRs/Issues
None found yet, but this might be related to a broader design choice about performance vs. flexibility in C-implemented functions.
Proposed Patch
A patch would need to modify the C code for _functools_reduce (likely in Modules/_functools.c) to:
Change its calling convention to one that supports keyword arguments (e.g., METH_FASTCALL | METH_KEYWORDS).
Parse arguments using PyArg_ParseTupleAndKeywords (or a similar API) to accept function, sequence, and initial either by position or by keyword.
Crucially, maintain 100% backward compatibility with the existing positional argument calling style.
Alternatively, a simpler approach is to make only the initial parameter accessible via keyword, which might be sufficient for the primary use case.
Activity
skirpichev commented on Sep 18, 2025
The initial parameter is accepted by name now. I don't think that other parameters should be modified that way to justify performance penalty.