Skip to content

Commit ee67a6b

Browse files
committed
BUG: wrap all supported inplace methods to avoid making a copy (pandas-dev#12962)
1 parent 6630c4e commit ee67a6b

File tree

2 files changed

+39
-3
lines changed

2 files changed

+39
-3
lines changed

pandas/core/ops.py

+12-3
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,10 @@ def add_special_arithmetic_methods(cls, arith_method=None,
186186
arith_method : function (optional)
187187
factory for special arithmetic methods, with op string:
188188
f(op, name, str_rep, default_axis=None, fill_zeros=None, **eval_kwargs)
189-
comp_method : function, optional,
189+
comp_method : function (optional)
190190
factory for rich comparison - signature: f(op, name, str_rep)
191+
bool_method : function (optional)
192+
factory for boolean methods - signature: f(op, name, str_rep)
191193
use_numexpr : bool, default True
192194
whether to accelerate with numexpr, defaults to True
193195
force : bool, default False
@@ -234,9 +236,16 @@ def f(self, other):
234236
__isub__=_wrap_inplace_method(new_methods["__sub__"]),
235237
__imul__=_wrap_inplace_method(new_methods["__mul__"]),
236238
__itruediv__=_wrap_inplace_method(new_methods["__truediv__"]),
237-
__ipow__=_wrap_inplace_method(new_methods["__pow__"]), ))
239+
__ifloordiv__=_wrap_inplace_method(new_methods["__floordiv__"]),
240+
__imod__=_wrap_inplace_method(new_methods["__mod__"]),
241+
__ipow__=_wrap_inplace_method(new_methods["__pow__"])))
238242
if not compat.PY3:
239-
new_methods["__idiv__"] = new_methods["__div__"]
243+
new_methods["__idiv__"] = _wrap_inplace_method(new_methods["__div__"])
244+
if bool_method:
245+
new_methods.update(
246+
dict(__iand__=_wrap_inplace_method(new_methods["__and__"]),
247+
__ior__=_wrap_inplace_method(new_methods["__or__"]),
248+
__ixor__=_wrap_inplace_method(new_methods["__xor__"])))
240249

241250
add_methods(cls, new_methods=new_methods, force=force, select=select,
242251
exclude=exclude)

pandas/tests/frame/test_operators.py

+27
Original file line numberDiff line numberDiff line change
@@ -1161,6 +1161,33 @@ def test_inplace_ops_identity(self):
11611161
assert_frame_equal(df2, expected)
11621162
assert df._data is df2._data
11631163

1164+
@pytest.mark.parametrize('op', ['add', 'and', 'div', 'floordiv', 'mod',
1165+
'mul', 'or', 'pow', 'sub', 'truediv',
1166+
'xor'])
1167+
def test_inplace_ops_identity2(self, op):
1168+
1169+
if compat.PY3 and op == 'div':
1170+
return
1171+
1172+
df = DataFrame({'a': [1., 2., 3.],
1173+
'b': [1, 2, 3]})
1174+
1175+
operand = 2
1176+
if op in ('and', 'or', 'xor'):
1177+
# cannot use floats for boolean ops
1178+
df['a'] = [True, False, True]
1179+
1180+
df_copy = df.copy()
1181+
iop = '__i{}__'.format(op)
1182+
op = '__{}__'.format(op)
1183+
1184+
# no id change and value is correct
1185+
getattr(df, iop)(operand)
1186+
expected = getattr(df_copy, op)(operand)
1187+
assert_frame_equal(df, expected)
1188+
expected = id(df)
1189+
assert id(df) == expected
1190+
11641191
def test_alignment_non_pandas(self):
11651192
index = ['A', 'B', 'C']
11661193
columns = ['X', 'Y', 'Z']

0 commit comments

Comments
 (0)