Skip to content

functools.reduce does not accept keyword arguments for its parameters #139114

@SaiAkaruiAo

Description

@SaiAkaruiAo

Bug report

Bug description:

Description

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

  1. Import reduce and partial from functools, and import an operator (e.g., operator.mul).
  2. Attempt to create a partial function for reduction using keyword arguments.
  3. Observe the TypeError.

Code Example:

from functools import reduce, partial
import operator

result1 = reduce(operator.mul, [2, 3, 5], 1) # 30, without problem

# Attempt to create a product reducer with initializer=1 using partial
product_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:

product_int = partial(reduce, operator.mul, initial=1)
result = product_int([2, 3, 5])  # Returns 30

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:

def reduce(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

  1. 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.
  2. 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.
  3. 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:
    product_int = lambda seq: reduce(operator.mul, seq, 1)
  • 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:

  1. Change its calling convention to one that supports keyword arguments (e.g., METH_FASTCALL | METH_KEYWORDS).
  2. Parse arguments using PyArg_ParseTupleAndKeywords (or a similar API) to accept function, sequence, and initial either by position or by keyword.
  3. 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.

CPython versions tested on:

3.12

Operating systems tested on:

Windows

Metadata

Metadata

Assignees

No one assigned

    Labels

    type-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions