Skip to main content

Lib/operator.py

Source:

cpython 3.14 @ ab2d84fe1023/Lib/operator.py

operator.py is the pure-Python reference implementation of the operator module. At runtime the C extension _operator replaces most of it via from _operator import * near the bottom of the file. What remains as non-replaceable Python: the dunder-name aliases (__add__ = add, etc.) assigned after the import, and the class bodies of attrgetter, itemgetter, and methodcaller, which the C version mirrors in structure.

Map

LinesSymbolPurpose
1–25module header__all__, imports
26–55lt, le, eq, ne, ge, gtSix comparison wrappers
56–80not_, truth, is_, is_notLogical and identity predicates
81–140abs through xorArithmetic, bitwise, and matmul operators
141–190iadd through ixorAugmented assignment operators
191–215concat, contains, delitem, getitem, setitemSequence operations
216–230length_hint__length_hint__ protocol fallback
231–235callFirst-class callable wrapper
236–265attrgetterDotted attribute access as a callable
266–290itemgetterSubscript access as a callable
291–315methodcallerNamed method call as a callable
316–320dunder aliases__lt__ = lt, __add__ = add, etc.

Reading

Comparison wrappers: eq, ne, lt, le, gt, ge

The six comparison functions are thin wrappers that make Python's comparison operators available as first-class callables. They are picklable by name and composable with map, filter, and sorted.

# CPython: Lib/operator.py:26 lt
def lt(a, b):
"Same as a < b."
return a < b

# CPython: Lib/operator.py:34 eq
def eq(a, b):
"Same as a == b."
return a == b

ne is not defined as not eq(a, b) because __ne__ and __eq__ are independent slots. An object may override __ne__ to return something other than not self.__eq__(other).

Augmented assignment operators: iadd, isub, imul and aliases

The i-prefixed functions implement in-place operators. They modify the left operand when the type supports it (mutable containers) and return the result, which may be the same object or a new one (for immutable types).

# CPython: Lib/operator.py:158 iadd
def iadd(a, b):
"Same as a += b."
a += b
return a

# CPython: Lib/operator.py:163 imul
def imul(a, b):
"Same as a *= b."
a *= b
return a

Each augmented function also gets a dunder alias after the _operator import: __iadd__ = iadd, __imul__ = imul, etc. The alias block is what makes these picklable under both names.

attrgetter, itemgetter, methodcaller objects

All three are callable classes whose __reduce__ methods support pickling. The constructor precomputes a _call closure to avoid attribute lookup overhead on each invocation.

attrgetter splits dotted paths at construction time and chains getattr calls in a loop:

# CPython: Lib/operator.py:243 attrgetter.__init__ single-attr path
def __init__(self, attr, /, *attrs):
if not attrs:
if not isinstance(attr, str):
raise TypeError('attribute name must be a string')
self._attrs = (attr,)
names = attr.split('.')
def func(obj):
for name in names:
obj = getattr(obj, name)
return obj
self._call = func

itemgetter wraps one or more subscript operations. With a single key it returns the item directly; with multiple keys it returns a tuple:

# CPython: Lib/operator.py:272 itemgetter.__call__ multi-item path
def __call__(self, obj):
return tuple(obj[i] for i in self._items)

methodcaller stores the method name, positional args, and keyword args. Its __reduce__ has a special case for keyword arguments: because (class, (name,) + args) cannot encode **kwargs, it wraps the reconstruction in a functools.partial:

# CPython: Lib/operator.py:309 methodcaller.__reduce__
def __reduce__(self):
if not self._kwargs:
return self.__class__, (self._name,) + self._args
else:
from functools import partial
return partial(self.__class__, self._name, **self._kwargs), self._args

__all__ and the double-underscore aliasing

The __all__ list at the top of the file enumerates both the plain names (add, mul) and the dunder aliases (__add__, __mul__). After the from _operator import * line, the C-backed names occupy the same module namespace. The alias block at the bottom then points every dunder name at the corresponding plain name:

# CPython: Lib/operator.py:316 dunder alias block (excerpt)
__lt__ = lt
__le__ = le
__eq__ = eq
__ne__ = ne
__ge__ = ge
__gt__ = gt
__add__ = add
__mul__ = mul
__matmul__ = matmul
# ... continues for all operators

This ensures that operator.__add__ and operator.add are the same object after import. Both names survive a pickle round-trip because both appear in the module's __dict__.

gopy notes

Status: not yet ported.

Planned package path: module/operator/.

The comparison wrappers (lt, le, eq, ne, ge, gt) are a direct translation into Go functions that call the corresponding objects/protocol.go comparison entries. The iadd / imul family must return the augmented object, which for immutable types will be a new allocation. attrgetter, itemgetter, and methodcaller each need a Go struct with Call and Reduce methods; Reduce on methodcaller with kwargs will need the functools.partial object from module/functools/. length_hint is used in several allocation hot paths (objects/list.go, objects/bytes.go) and should be ported before the container types that call it.