methods.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. """
  2. Functions to generate methods and pin them to the appropriate classes.
  3. """
  4. from __future__ import annotations
  5. import operator
  6. from pandas.core.dtypes.generic import (
  7. ABCDataFrame,
  8. ABCSeries,
  9. )
  10. from pandas.core.ops import roperator
  11. def _get_method_wrappers(cls):
  12. """
  13. Find the appropriate operation-wrappers to use when defining flex/special
  14. arithmetic, boolean, and comparison operations with the given class.
  15. Parameters
  16. ----------
  17. cls : class
  18. Returns
  19. -------
  20. arith_flex : function or None
  21. comp_flex : function or None
  22. """
  23. # TODO: make these non-runtime imports once the relevant functions
  24. # are no longer in __init__
  25. from pandas.core.ops import (
  26. flex_arith_method_FRAME,
  27. flex_comp_method_FRAME,
  28. flex_method_SERIES,
  29. )
  30. if issubclass(cls, ABCSeries):
  31. # Just Series
  32. arith_flex = flex_method_SERIES
  33. comp_flex = flex_method_SERIES
  34. elif issubclass(cls, ABCDataFrame):
  35. arith_flex = flex_arith_method_FRAME
  36. comp_flex = flex_comp_method_FRAME
  37. return arith_flex, comp_flex
  38. def add_flex_arithmetic_methods(cls) -> None:
  39. """
  40. Adds the full suite of flex arithmetic methods (``pow``, ``mul``, ``add``)
  41. to the class.
  42. Parameters
  43. ----------
  44. cls : class
  45. flex methods will be defined and pinned to this class
  46. """
  47. flex_arith_method, flex_comp_method = _get_method_wrappers(cls)
  48. new_methods = _create_methods(cls, flex_arith_method, flex_comp_method)
  49. new_methods.update(
  50. {
  51. "multiply": new_methods["mul"],
  52. "subtract": new_methods["sub"],
  53. "divide": new_methods["div"],
  54. }
  55. )
  56. # opt out of bool flex methods for now
  57. assert not any(kname in new_methods for kname in ("ror_", "rxor", "rand_"))
  58. _add_methods(cls, new_methods=new_methods)
  59. def _create_methods(cls, arith_method, comp_method):
  60. # creates actual flex methods based upon arithmetic, and comp method
  61. # constructors.
  62. have_divmod = issubclass(cls, ABCSeries)
  63. # divmod is available for Series
  64. new_methods = {}
  65. new_methods.update(
  66. {
  67. "add": arith_method(operator.add),
  68. "radd": arith_method(roperator.radd),
  69. "sub": arith_method(operator.sub),
  70. "mul": arith_method(operator.mul),
  71. "truediv": arith_method(operator.truediv),
  72. "floordiv": arith_method(operator.floordiv),
  73. "mod": arith_method(operator.mod),
  74. "pow": arith_method(operator.pow),
  75. "rmul": arith_method(roperator.rmul),
  76. "rsub": arith_method(roperator.rsub),
  77. "rtruediv": arith_method(roperator.rtruediv),
  78. "rfloordiv": arith_method(roperator.rfloordiv),
  79. "rpow": arith_method(roperator.rpow),
  80. "rmod": arith_method(roperator.rmod),
  81. }
  82. )
  83. new_methods["div"] = new_methods["truediv"]
  84. new_methods["rdiv"] = new_methods["rtruediv"]
  85. if have_divmod:
  86. # divmod doesn't have an op that is supported by numexpr
  87. new_methods["divmod"] = arith_method(divmod)
  88. new_methods["rdivmod"] = arith_method(roperator.rdivmod)
  89. new_methods.update(
  90. {
  91. "eq": comp_method(operator.eq),
  92. "ne": comp_method(operator.ne),
  93. "lt": comp_method(operator.lt),
  94. "gt": comp_method(operator.gt),
  95. "le": comp_method(operator.le),
  96. "ge": comp_method(operator.ge),
  97. }
  98. )
  99. new_methods = {k.strip("_"): v for k, v in new_methods.items()}
  100. return new_methods
  101. def _add_methods(cls, new_methods) -> None:
  102. for name, method in new_methods.items():
  103. setattr(cls, name, method)