Inzynierka/Lib/site-packages/pandas/core/ops/methods.py

125 lines
3.7 KiB
Python
Raw Normal View History

2023-06-02 12:51:02 +02:00
"""
Functions to generate methods and pin them to the appropriate classes.
"""
from __future__ import annotations
import operator
from pandas.core.dtypes.generic import (
ABCDataFrame,
ABCSeries,
)
from pandas.core.ops import roperator
def _get_method_wrappers(cls):
"""
Find the appropriate operation-wrappers to use when defining flex/special
arithmetic, boolean, and comparison operations with the given class.
Parameters
----------
cls : class
Returns
-------
arith_flex : function or None
comp_flex : function or None
"""
# TODO: make these non-runtime imports once the relevant functions
# are no longer in __init__
from pandas.core.ops import (
flex_arith_method_FRAME,
flex_comp_method_FRAME,
flex_method_SERIES,
)
if issubclass(cls, ABCSeries):
# Just Series
arith_flex = flex_method_SERIES
comp_flex = flex_method_SERIES
elif issubclass(cls, ABCDataFrame):
arith_flex = flex_arith_method_FRAME
comp_flex = flex_comp_method_FRAME
return arith_flex, comp_flex
def add_flex_arithmetic_methods(cls) -> None:
"""
Adds the full suite of flex arithmetic methods (``pow``, ``mul``, ``add``)
to the class.
Parameters
----------
cls : class
flex methods will be defined and pinned to this class
"""
flex_arith_method, flex_comp_method = _get_method_wrappers(cls)
new_methods = _create_methods(cls, flex_arith_method, flex_comp_method)
new_methods.update(
{
"multiply": new_methods["mul"],
"subtract": new_methods["sub"],
"divide": new_methods["div"],
}
)
# opt out of bool flex methods for now
assert not any(kname in new_methods for kname in ("ror_", "rxor", "rand_"))
_add_methods(cls, new_methods=new_methods)
def _create_methods(cls, arith_method, comp_method):
# creates actual flex methods based upon arithmetic, and comp method
# constructors.
have_divmod = issubclass(cls, ABCSeries)
# divmod is available for Series
new_methods = {}
new_methods.update(
{
"add": arith_method(operator.add),
"radd": arith_method(roperator.radd),
"sub": arith_method(operator.sub),
"mul": arith_method(operator.mul),
"truediv": arith_method(operator.truediv),
"floordiv": arith_method(operator.floordiv),
"mod": arith_method(operator.mod),
"pow": arith_method(operator.pow),
"rmul": arith_method(roperator.rmul),
"rsub": arith_method(roperator.rsub),
"rtruediv": arith_method(roperator.rtruediv),
"rfloordiv": arith_method(roperator.rfloordiv),
"rpow": arith_method(roperator.rpow),
"rmod": arith_method(roperator.rmod),
}
)
new_methods["div"] = new_methods["truediv"]
new_methods["rdiv"] = new_methods["rtruediv"]
if have_divmod:
# divmod doesn't have an op that is supported by numexpr
new_methods["divmod"] = arith_method(divmod)
new_methods["rdivmod"] = arith_method(roperator.rdivmod)
new_methods.update(
{
"eq": comp_method(operator.eq),
"ne": comp_method(operator.ne),
"lt": comp_method(operator.lt),
"gt": comp_method(operator.gt),
"le": comp_method(operator.le),
"ge": comp_method(operator.ge),
}
)
new_methods = {k.strip("_"): v for k, v in new_methods.items()}
return new_methods
def _add_methods(cls, new_methods) -> None:
for name, method in new_methods.items():
setattr(cls, name, method)