python
April 5, 2023

Изменяемость объектов в 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 ожидаемо остался без изменений.