1""" 2Boilerplate functions used in defining binary operations. 3""" 4from functools import wraps 5from typing import Callable 6 7from pandas._libs.lib import item_from_zerodim 8from pandas._typing import F 9 10from pandas.core.dtypes.generic import ABCDataFrame, ABCIndexClass, ABCSeries 11 12 13def unpack_zerodim_and_defer(name: str) -> Callable[[F], F]: 14 """ 15 Boilerplate for pandas conventions in arithmetic and comparison methods. 16 17 Parameters 18 ---------- 19 name : str 20 21 Returns 22 ------- 23 decorator 24 """ 25 26 def wrapper(method: F) -> F: 27 return _unpack_zerodim_and_defer(method, name) 28 29 return wrapper 30 31 32def _unpack_zerodim_and_defer(method, name: str): 33 """ 34 Boilerplate for pandas conventions in arithmetic and comparison methods. 35 36 Ensure method returns NotImplemented when operating against "senior" 37 classes. Ensure zero-dimensional ndarrays are always unpacked. 38 39 Parameters 40 ---------- 41 method : binary method 42 name : str 43 44 Returns 45 ------- 46 method 47 """ 48 is_cmp = name.strip("__") in {"eq", "ne", "lt", "le", "gt", "ge"} 49 50 @wraps(method) 51 def new_method(self, other): 52 53 if is_cmp and isinstance(self, ABCIndexClass) and isinstance(other, ABCSeries): 54 # For comparison ops, Index does *not* defer to Series 55 pass 56 else: 57 for cls in [ABCDataFrame, ABCSeries, ABCIndexClass]: 58 if isinstance(self, cls): 59 break 60 if isinstance(other, cls): 61 return NotImplemented 62 63 other = item_from_zerodim(other) 64 65 return method(self, other) 66 67 return new_method 68 69 70def get_op_result_name(left, right): 71 """ 72 Find the appropriate name to pin to an operation result. This result 73 should always be either an Index or a Series. 74 75 Parameters 76 ---------- 77 left : {Series, Index} 78 right : object 79 80 Returns 81 ------- 82 name : object 83 Usually a string 84 """ 85 if isinstance(right, (ABCSeries, ABCIndexClass)): 86 name = _maybe_match_name(left, right) 87 else: 88 name = left.name 89 return name 90 91 92def _maybe_match_name(a, b): 93 """ 94 Try to find a name to attach to the result of an operation between 95 a and b. If only one of these has a `name` attribute, return that 96 name. Otherwise return a consensus name if they match of None if 97 they have different names. 98 99 Parameters 100 ---------- 101 a : object 102 b : object 103 104 Returns 105 ------- 106 name : str or None 107 108 See Also 109 -------- 110 pandas.core.common.consensus_name_attr 111 """ 112 a_has = hasattr(a, "name") 113 b_has = hasattr(b, "name") 114 if a_has and b_has: 115 if a.name == b.name: 116 return a.name 117 else: 118 # TODO: what if they both have np.nan for their names? 119 return None 120 elif a_has: 121 return a.name 122 elif b_has: 123 return b.name 124 return None 125