0

我正在创建一个数据提供者类,它将保存数据、执行转换并使其可用于其他类。

如果用户创建这个类的实例并在实例化时传递一些数据,我想将它存储两次:一次用于所有转换,一次作为原始数据的副本。让我们假设数据本身有一个copy方法。

我正在使用attrs包来创建类,但通常也会对最好的方法感兴趣(也许有更好的方法来获得我想要的东西?)

这是我到目前为止所拥有的:

@attr.s
class DataContainer(object):
    """Interface for managing data. Reads and write data, acts as a provider to other classes.
    """

    data = attr.ib(default=attr.Factory(list))
    data_copy = data.copy()

    def my_func(self, param1='all'):
        """Do something useful"""
        return param1

这不起作用:AttributeError: '_CountingAttr' object has no attribute 'copy'

我也不能打电话data_copy = self.data.copy(),我得到错误:NameError: name 'self' is not defined

没有包的工作等效项attrs是:

class DataContainer(object):
    """Interface for managing data. Reads and write data, acts as a provider to other classes.
    """
    def __init__(self, data):
        "Init method, saving passed data and a backup copy"
        self.data = data
        self.data_copy = data

编辑:

正如@hynek 所指出的,我上面的简单 init 方法需要更正以制作数据的实际副本:即self.data_copy = data.copy(). 否则两者self.data都会self.data_copy指向同一个对象。

4

2 回答 2

1

你可以在这里做两件事。

您发现自己的第一个:您使用__attr_post_init__.

第二个是有一个默认值:

>>> import attr
>>> @attr.s
... class C:
...     x = attr.ib()
...     _x_backup = attr.ib()
...     @_x_backup.default
...     def _copy_x(self):
...         return self.x.copy()
>>> l = [1, 2, 3]
>>> i = C(l)
>>> i
C(x=[1, 2, 3], _x_backup=[1, 2, 3])
>>> i.x.append(4)
>>> i
C(x=[1, 2, 3, 4], _x_backup=[1, 2, 3])

JFTR,你的例子

def __init__(self, data):
    self.data = data
    self.data_copy = data

是错误的,因为您将两次分配相同的对象,这意味着修改self.data也会修改self.data_copy,反之亦然。

于 2018-03-22T06:49:02.150 回答
0

更深入地查看文档后(向右滚动到底部),我发现有一种用于创建的类的 post-init 钩子attrs

除了简单的赋值之外,您可以只包含一个特殊的__attrs_post_init__方法,该方法可以在方法中完成更复杂的事情。__init__

这是我的最终工作代码:

In [1]: @attr.s
     ...: class DataContainer(object):
     ...:    """Interface for managing data. Reads and write data,
     ...:    acts as a provider to other classes.
     ...:    """
     ...: 
     ...:    data = attr.ib()
     ...: 
     ...:    def __attrs_post_init__(self):
     ...:        """Perform additional init work on instantiation.
     ...:        Make a copy of the raw input data.
     ...:        """
     ...:        self.data_copy = self.data.copy()



In [2]: some_data = np.array([[1, 2, 3], [4, 5, 6]])

In [3]: foo = DataContainer(some_data)

In [4]: foo.data
Out[5]: 
array([[1, 2, 3],
       [4, 5, 6]])

In [6]: foo.data_copy
Out[7]: 
array([[1, 2, 3],
       [4, 5, 6]])

为了更加确定,我检查了这两个属性没有引用同一个对象。在这种情况下,它们不是,这可能要归功于copyNumPy 数组上的方法。

In [8]: foo.data[0,0] = 999

In [9]: foo.data
Out[10]: 
array([[999,   2,   3],
       [  4,   5,   6]])

In [11]: foo.data_copy
Out[12]: 
array([[1, 2, 3],
       [4, 5, 6]])
于 2018-03-14T21:39:17.933 回答