Repository: pandas-dev/pandas

Issue:
REGR: DataFrame.eval not respecting inplace argument in pandas 1.4
```python
import pandas as pd
import numpy as np

# Example for eval
pdf = pd.DataFrame({"A": range(1, 6), "B": range(10, 0, -2), "C": range(11, 16)})
pser = pdf.A
pdf.eval("A = B + C", inplace=True)
# pser != pdf.A after 1.4
```

_Originally posted by @Yikun in https://github.com/pandas-dev/pandas/issues/47188#issuecomment-1148168713_


Discussion:
Thanks!
moving to 1.4.4
first bad commit: [ [`03dd698`](https://github.com/pandas-dev/pandas/commit/03dd698bc1e84c35aba8b51bdd45c472860b9ec3) ] BUG: DataFrame.__setitem__ sometimes operating inplace (https://github.com/pandas-dev/pandas/pull/43406)

cc @jbrockmendel

Patch:
diff --git a/doc/source/whatsnew/v1.4.4.rst b/doc/source/whatsnew/v1.4.4.rst
index 57b8fdee5888a..1e7ed256c05ef 100644
--- a/doc/source/whatsnew/v1.4.4.rst
+++ b/doc/source/whatsnew/v1.4.4.rst
@@ -19,6 +19,7 @@ Fixed regressions
 - Fixed regression in :meth:`DataFrame.loc` not updating the cache correctly after values were set (:issue:`47867`)
 - Fixed regression in :meth:`DataFrame.loc` not aligning index in some cases when setting a :class:`DataFrame` (:issue:`47578`)
 - Fixed regression in setting ``None`` or non-string value into a ``string``-dtype Series using a mask (:issue:`47628`)
+- Fixed regression in :meth:`DataFrame.eval` creating a copy when updating inplace (:issue:`47449`)
 -
 
 .. ---------------------------------------------------------------------------
diff --git a/pandas/core/computation/eval.py b/pandas/core/computation/eval.py
index fa0ef46b850d1..0a9bb6a4bb6fa 100644
--- a/pandas/core/computation/eval.py
+++ b/pandas/core/computation/eval.py
@@ -19,6 +19,7 @@
 )
 from pandas.core.computation.parsing import tokenize_string
 from pandas.core.computation.scope import ensure_scope
+from pandas.core.generic import NDFrame
 
 from pandas.io.formats.printing import pprint_thing
 
@@ -387,7 +388,10 @@ def eval(
             try:
                 with warnings.catch_warnings(record=True):
                     # TODO: Filter the warnings we actually care about here.
-                    target[assigner] = ret
+                    if inplace and isinstance(target, NDFrame):
+                        target.loc[:, assigner] = ret
+                    else:
+                        target[assigner] = ret
             except (TypeError, IndexError) as err:
                 raise ValueError("Cannot assign expression output to target") from err
 


Test patch:
diff --git a/pandas/tests/computation/test_eval.py b/pandas/tests/computation/test_eval.py
index 672a87edbad66..b3b80e8e5db65 100644
--- a/pandas/tests/computation/test_eval.py
+++ b/pandas/tests/computation/test_eval.py
@@ -1912,6 +1912,26 @@ def test_eval_no_support_column_name(request, column):
     tm.assert_frame_equal(result, expected)
 
 
+@td.skip_array_manager_not_yet_implemented
+def test_set_inplace(using_copy_on_write):
+    # https://github.com/pandas-dev/pandas/issues/47449
+    # Ensure we don't only update the DataFrame inplace, but also the actual
+    # column values, such that references to this column also get updated
+    df = DataFrame({"A": [1, 2, 3], "B": [4, 5, 6], "C": [7, 8, 9]})
+    result_view = df[:]
+    ser = df["A"]
+    df.eval("A = B + C", inplace=True)
+    expected = DataFrame({"A": [11, 13, 15], "B": [4, 5, 6], "C": [7, 8, 9]})
+    tm.assert_frame_equal(df, expected)
+    if not using_copy_on_write:
+        tm.assert_series_equal(ser, expected["A"])
+        tm.assert_series_equal(result_view["A"], expected["A"])
+    else:
+        expected = Series([1, 2, 3], name="A")
+        tm.assert_series_equal(ser, expected)
+        tm.assert_series_equal(result_view["A"], expected)
+
+
 class TestValidate:
     @pytest.mark.parametrize("value", [1, "True", [1, 2, 3], 5.0])
     def test_validate_bool_args(self, value):