Python 为什么复制数据帧后属性会丢失

Python 为什么复制数据帧后属性会丢失,python,Python,为什么不可能通过副本传递实例的属性?我想将name属性传递给另一个数据帧 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 ...

为什么不可能通过副本传递实例的属性?我想将
name
属性传递给另一个数据帧

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'
类似地,在创建类并从中继承时,为什么这也不起作用

class DfWithName(pd.DataFrame):

    def __init__(self, *args, **kwargs):
        self.__init__ = super().__init__(*args, **kwargs)
        print('lol')

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value
并使用相同的代码:

import copy
df = DfWithName([1,2,3])
df.name = 'sheet1'
df2 = copy.deepcopy(df) 
print(f'df.name: {df2.name}')
>>    AttributeError    
        ...      
      'DataFrame' object has no attribute 'name'

熊猫似乎不支持将自定义元数据附加到数据帧。请参阅(可能重复?)和。

此代码工作:

>>> class test():
...     @property
...     def name(self):
...         return self._name
...     @name.setter
...     def name(self, value):
...         self._name = value
...
>>>
>>> a = test()
>>> a.name = 'Test123'
>>> import copy
>>> a2 = copy.deepcopy(a)
>>> print(a2.name)
Test123
所以我认为行为是由
pd.DataFrame

我发现pandas定义了函数
\uuuu deepcopy\uuuu
,但我不能完全理解原因

如果在MRO中找到,将使用自定义的
\uuuu deepcopy\uuuu
方法,该方法可能会返回它喜欢的任何结果(包括完全虚假的结果)。事实上,数据帧实现了一种
\uuuu deepcopy\uuu
方法:

def __deepcopy__(self, memo=None):
    if memo is None:
        memo = {}
    return self.copy(deep=True)
它将委托给
self.copy
,您将在其中找到:

您将在(合并在中)中找到:

\uuuuu deepcopy\uuuuuu
现在返回数据的浅拷贝(当前为视图)——允许更改元数据


相关问题:.

如其他地方所述,
DataFrame
类有一个自定义方法,它不一定像普通对象那样复制分配给实例的任意属性

有趣的是,有一个内部的
\u元数据
属性,它似乎可以列出复制/序列化
NDFrame
时应保留的其他属性。这里讨论了这一点:

不幸的是,这仍然被认为是一个未记录的内部细节,所以它可能不应该被使用。通过查看代码,原则上您可以执行以下操作:

mydf = pd.DataFrame(...)
mydf.name = 'foo'
mydf._metadata += ['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)
如果您为现有的
copy
方法提供了自己的包装,并且可能还提供了
\uuuuu getstate\uuuuuuu
\uuuu setstate\uuuuu
,那么您也可以在不依赖此内部
\uu metadata
属性的情况下执行此操作


更新:现在似乎已经开始使用
\u metadata
属性来扩展熊猫类了。因此,上面的例子应该或多或少起作用。这些文件更多的是为了熊猫本身的发展,所以它可能仍然有点不稳定。但熊猫本身就是这样扩展
NDFrame

的子类的,也许我发现了一个错误的复制。。。关键是您的新属性只属于这个特定实例,它不记录在类级别。因此,
copy
无法找到它@这并不完全准确。对于大多数实例,
copy.deepcopy()
将复制其
\uuuu dict\uuuu
成员,因此,如果将某些属性分配给实例,然后复制该实例,则将复制其属性。更可能的是,
DataFrame
,作为一个相当普通的类,可能有一些自定义的序列化/深度复制实现,它不知道附加到它的任意属性。事实上,
NDFrame
(它是
DataFrame
的基类)有这个自定义的
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
:是的,更准确。您可以重写
\uuuu deepcopy\uuuuu
,它只记录与类相关的信息并不奇怪。使用
df.copy(deep=True)
也不能缓解问题。我现在知道元数据副本没有实现,但我想知道是什么python机制导致了这种情况。是的,使用
\u metadata
属性hack非常适合我的用例。请注意我对示例代码的更新。我还没有测试过它,但我想你可能需要像上面的
\u构造函数那样的东西。似乎你没有在第一个链接中链接答案,这与你后来链接的github问题相同。如果可以,请编辑您的答案,以包含相关的,可能是重复的答案。@JimFasarakisHilliard谢谢,已更新。虽然我很确定这不是重复,但OP的问题更具体
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)