为什么不能通过副本传递实例的属性?我想将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'
答案 0 :(得分:8)
如其他地方所述,DataFrame
类有一个自定义__deepcopy__
方法,它不一定复制分配给实例的任意属性,就像普通对象一样。
有趣的是,有一个内部_metadata
属性似乎能够列出NDFrame
的其他属性,这些属性在复制/序列化时应该保留。这里将对此进行讨论:https://github.com/pandas-dev/pandas/issues/9317
不幸的是,这仍然被认为是未记录的内部细节,因此可能不应该使用它。通过查看代码,您原则上可以:
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)
如果您为现有的_metadata
方法提供自己的包装器,并且可能还提供copy
和__getstate__
,则也可以在不依赖此内部__setstate__
属性的情况下执行此操作。
更新:实际上使用_metadata
属性来扩展Pandas类现在是documented。所以上面的例子应该或多或少有效。这些文档更多用于开发Pandas本身,因此它可能仍然有点不稳定。但这就是Pandas本身如何扩展NDFrame
的子类。
答案 1 :(得分:3)
copy.deepcopy
将使用自定义__deepcopy__
方法,如果它在MRO中找到,可以返回任何喜欢的内容(包括完全虚假的结果)。实际上,数据帧实现了__deepcopy__
方法:
def __deepcopy__(self, memo=None):
if memo is None:
memo = {}
return self.copy(deep=True)
它委托给self.copy
,您会在其中找到this note in the docstring:
Notes
-----
When ``deep=True``, data is copied but actual Python objects
will not be copied recursively, only the reference to the object.
This is in contrast to `copy.deepcopy` in the Standard Library,
which recursively copies object data (see examples below).
您会在v0.13 release notes(合并于PR 4039)中找到:
__deepcopy__
现在返回数据的浅表副本(当前:视图) - 允许更改元数据。
相关问题:17406。
答案 2 :(得分:1)
将自定义元数据附加到DataFrames似乎不支持pandas。请参阅this answer(可能重复?)和this github issue。
答案 3 :(得分:-2)
此代码有效:
>>> 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定义了函数__deepcopy__
,但我不能完全理解它的原因。