Skip to content

Commit 9eaf50b

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

File tree

2 files changed

+38
-3
lines changed

2 files changed

+38
-3
lines changed

pandas/core/ops.py

Lines changed: 12 additions & 3 deletions
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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,6 +1161,32 @@ 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', 'mul',
1165+
'or', 'pow', 'sub', 'truediv', 'xor'])
1166+
def test_inplace_ops_identity2(self, op):
1167+
1168+
if compat.PY3 and op == 'div':
1169+
return
1170+
1171+
df = DataFrame({'a': [1., 2., 3.],
1172+
'b': [1, 2, 3]})
1173+
1174+
operand = 2
1175+
if op in ('and', 'or', 'xor'):
1176+
# cannot use floats for boolean ops
1177+
df['a'] = [True, False, True]
1178+
1179+
df_copy = df.copy()
1180+
iop = '__i{}__'.format(op)
1181+
op = '__{}__'.format(op)
1182+
1183+
# no id change and value is correct
1184+
getattr(df, iop)(operand)
1185+
expected = getattr(df_copy, op)(operand)
1186+
assert_frame_equal(df, expected)
1187+
expected = id(df)
1188+
assert id(df) == expected
1189+
11641190
def test_alignment_non_pandas(self):
11651191
index = ['A', 'B', 'C']
11661192
columns = ['X', 'Y', 'Z']

0 commit comments

Comments
 (0)