Pandas DataFrame: копирование произвольных атрибутов с помощью copy.deepcopy ()

Разбираемся, почему такой код не работает:

import copy
df = pd.DataFrame([1,2,3])
df.name = 'sheet1'
df2 = copy.deepcopy(df)

print(f'df.name: {df.name}')
>> df.name: sheet1

print(f'df2.name: {df2.name}')
>>    AttributeError    
        ...      
      'DataFrame' object has no attribute 'name'

Класс DataFrame имеет собственный метод __deepcopy__, который не копирует произвольные атрибуты, назначенные экземпляру, как у обычного объекта.

Оказывается, существует внутренний атрибут _metadata, который предназначен для отображения дополнительных атрибутов NDFrame, которые должны сохраняться при копировании или сериализации.

mydf = pd.DataFrame(...)
mydf.name = 'foo'
mydf._metadata += ['name']

И теперь при копировании с помощью copy.deepcopy() атрибут name не потеряется.

Можно создать производный от DataFrame класс, чтобы обеспечить такое поведение по умолчанию:

import functools

class NamedDataFrame(pd.DataFrame):
    _metadata = pd.DataFrame._metadata + ['name']

    def __init__(self, name, *args, **kwargs):
        self.name = name
        super().__init__(*args, **kwargs)

    @property
    def _constructor(self):
        return functools.partial(self.__class__, self.name)

Разработчики Pandas зафиксировали эту возможность в документации.

Еще один способ решить задачу — реализовать собственный декоратор для copy.deepcopy(), и, возможно, также запрограммировать методы __getstate__ и __setstate__.


Спасибо iguananaut.