Изменяемость объектов в Python: когда несет опасность и как от этого защититься
В мире нет ничего более постоянного, чем непостоянство (С. Батлер). Объекты в Python делятся на неизменяемые (целые, дробные, строчные, кортежи) и изменяемые (например, списки, словари, множества), в зависимости от чего их поведение при модификации может иметь неожиданный характер.
При изменении значения неизменяемого типа, создастся другой объект. Это можно установить путем вывода его идентификатора со встроенной функцией id:
a = 3 print(id(a)) a = 4 print(id(a))
В случае модификации списка, его идентификатор не поменяется, так как тип - изменяемый:
l = [1,2,3] print(id(l)) l[0]=4 print(id(l))
Это касается и Pandas датафрейма:
import pandas as pd df1 = pd.DataFrame([[0,1], [2,3]]) print(id(df1)) df1.iloc[0,0] = 4 print(id(df1))
Ввиду этого распространенной ошибкой является копирование датафрейма через присваивание:
df2 = df1 id(df2)
id объекта не меняется, и модификация одного сказывается на другом:
df2.iloc[0,0]=5 display(df2) display(df1)
Для получения копии датафрейма правильно использовать метод copy:
df3 = df1.copy() print(id(df3)) df3.iloc[0,0]=10 display(df3) display(df1)
Также будьте осторожны при модификации изменяемого объекта, передаваемого через аргумент в функцию:
def f(df, num): df.iloc[0,0] = num print(id(df)) f(df2, 14) display(df1) display(df2) display(df3)
f модифицировала df2, переданный в качестве аргумента. Также поменялся df1, так как он ссылается на тот же объект, а df3 ожидаемо остался без изменений.