【问题标题】:How can I change in Python the return/input type of a list that is implemented as an class attribute?如何在 Python 中更改作为类属性实现的列表的返回/输入类型?
【发布时间】:2012-02-17 13:45:37
【问题描述】:

编辑(将问题完全改写为原始版本(参见“原始版本”,稍后)具有误导性):

这里是设置:我有一个对象,它有一个类型的对象列表 <class 'One'>。我想访问此列表,但更愿意使用对象 <class 'Two'> 类型的 <class 'One'> 的丰富版本。

背景(一):

  • One 可以是一个可以通过 ORM 轻松存储的对象。 ORM 将根据数据模型处理列表
  • Two 是一个类似于 One 的对象,但丰富了许多功能或访问方式

背景(二):

  • 我尝试解决我向here 提出的与 SQLAlchemy 相关的问题。因此,当前问题的答案也可能是该问题更改 SQLAlchemy 列表的返回/输入类型的解决方案。

这里有一些代码用于说明:

import numpy as np

class One(object):
  """
  Data Transfere Object (DTO)
  """
  def __init__(self, name, data):
    assert type(name) == str
    assert type(data) == str
    self.name = name
    self.data = data

  def __repr__(self):
    return "%s(%r, %r)" %(self.__class__.__name__, self.name, self.data)

class Two(np.ndarray):
  _DTO = One
  def __new__(cls, name, data):
    dto = cls._DTO(name, data)
    return cls.newByDTO(dto)

  @classmethod
  def newByDTO(cls, dto):
    obj = np.fromstring(dto.data, dtype="float", sep=',').view(cls)
    obj.setflags(write=False) # Immutable
    obj._dto = dto
    return obj

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

class DataUI(object):
  def __init__(self, list_of_ones):
    for one in list_of_ones:
      assert type(one) == One
    self.list_of_ones = list_of_ones

if __name__ == '__main__':
  o1 = One('first object', "1, 3.0, 7, 8,1")
  o2 = One('second object', "3.7, 8, 10")
  my_data = DataUI ([o1, o2])

如何实现list_of_twos,它在list_of_ones 上运行,但为用户提供了一个包含Two 类型元素的列表:

type (my_data.list_of_twos[1]) == Two
>>> True
my_data.list_of_twos.append(Two("test", "1, 7, 4.5"))
print my_data.list_of_ones[-1]
>>> One('test', '1, 7, 4.5')

问题的原始版本:

以下是问题的说明:

class Data(object):
    def __init__(self, name, data_list):
        self.name = name
        self.data_list = data_list

if __name__ == '__main__':
    my_data = Data ("first data set", [0, 1, 1.4, 5])

我想通过另一个列表(例如my_data.data_np_list)访问my_data.data_list,该列表将列表元素作为不同类型(例如作为numpy.ndarray)处理:

>>> my_data.data_np_list[1]
array(1)
>>> my_data.data_np_list.append(np.array(7))
>>> print my_data.data_list
[0, 1, 1.4, 5, 7]

【问题讨论】:

  • 不确定我是否理解你的问题,但你可能想看看stackoverflow.com/questions/672172/…
  • 你缺少一个自我论证
  • 对不起,克里斯,我希望我现在的编辑没有影响到你的编辑……但是,是的,我提到了那个问题。
  • @PhilippderRautenberg:是我合并的编辑丢失了问题链接。我现在已经恢复了。
  • 不会修复它,但你的 __init__ 方法应该调用 super(Data, self).__init__()

标签: python list types


【解决方案1】:

你应该使用一个属性

class Data(object):
    def __init__(self, name, data_list):
        self.name = name
        self.data_list = data_list

    @property
    def data_np_list(self):
        return numpy.array(self.data_list)

if __name__ == '__main__':
    my_data = Data ("first data set", [0, 1, 1.4, 5])
    print my_data.data_np_list

edit:numpy 使用一个连续的内存区域。 python list 是链表。如果不付出性能成本,你就不能同时拥有这两者,这将使整个事情变得毫无用处。它们是不同的数据结构。

【讨论】:

  • 但这不会使它成为镜像,这是一个要求。
  • 这个解决方案的一个问题是我不能像普通列表那样访问data_np_list(例如:append)。此外,data_list 中的更改不会传播到 data_np_list
  • 所以您想让 numpy 数组的行为更像普通列表?现在我真的不明白你想要达到什么目的。
  • 问题如下:我有一个我想通过我的数据访问层(例如通过 SQLAlchemy)存储的特定对象的列表。另一方面 - 作为用户界面 - 我想使用该列表元素的拉皮条版本。也许我应该重新提出这个问题。
【解决方案2】:

不,你不能轻易做到(或者根本不会失去使用 numpy.array 可能获得的任何性能提升)。您想要两个完全不同的结构相互镜像,这意味着存储两者并在两者之间传输任何修改;将listnumpy.array 子类化以观察修改将是唯一的方法。

【讨论】:

  • 难道data_np_list 总是在data_list 上运行吗?这样我就不必让两个列表保持同步。我尽量避免子类化,因为我想将解决方案转移到我使用 SQLAlchemy 的工作中。因此,解决方案应该被封装。
  • 它们是完全不同的结构。所以你做不到。
【解决方案3】:

不确定你的方法是否正确。

属性获取器将有助于实现您正在做的事情。这是使用数组而不是 numpy 的类似方法。

我已将数组(或在您的情况下为 numpy 数据类型)设为内部表示,仅根据需要转换为列表并返回一个临时对象。

import unittest
import array

class GotAGetter(object):
  """Gets something.
  """
  def __init__(self, name, data_list):
    super(GotAGetter, self).__init__()
    self.name = name
    self.data_array = array.array('i', data_list)

  @property
  def data_list(self):
    return list(self.data_array)


class TestProperties(unittest.TestCase):
  def testProperties(self):
    data = [1,3,5]
    test = GotAGetter('fred', data)
    aString = str(test.data_array)
    lString = str(test.data_list) #Here you go.
    try:
      test.data_list = 'oops'
      self.fail('Should have had an attribute error by now')
    except AttributeError as exAttr:
      self.assertEqual(exAttr.message, "can't set attribute")
    self.assertEqual(aString, "array('i', [1, 3, 5])",
                     "The array doesn't look right")
    self.assertEqual(lString, '[1, 3, 5]',
                     "The list property doesn't look right")

if __name__ == "__main__":
  unittest.main()

【讨论】:

    【解决方案4】:

    我刚刚想出的一个解决方案是通过 ListView 类实现列表视图,该类采用以下参数:

    • raw_listOne-objects 列表
    • raw2new:将One-objects 转换为Two-objects 的函数
    • new2raw:将Two-objects 转换为One-objects 的函数

    代码如下:

    class ListView(list):
      def __init__(self, raw_list, raw2new, new2raw):
        self._data = raw_list
        self.converters = {'raw2new': raw2new,
            'new2raw': new2raw}
    
      def __repr__(self):
        repr_list = [self.converters['raw2new'](item) for item in self._data]
        repr_str = "["
        for element in repr_list:
          repr_str += element.__repr__() + ",\n "
        repr_str = repr_str[:-3] + "]"
        return repr_str
    
      def append(self, item):
        self._data.append(self.converters['new2raw'](item))
    
      def pop(self, index):
        self._data.pop(index)
    
      def __getitem__(self, index):
        return self.converters['raw2new'](self._data[index])
    
      def __setitem__(self, key, value):
        self._data.__setitem__(key, self.converters['new2raw'](value))
    
      def __delitem__(self, key):
        return self._data.__delitem__(key)
    
      def __getslice__(self, i, j):
        return ListView(self._data.__getslice__(i,j), **self.converters)
    
      def __contains__(self, item):
        return self._data.__contains__(self.converters['new2raw'](item))
    
      def __add__(self, other_list_view):
        assert self.converters == other_list_view.converters
        return ListView(
            self._data + other_list_view._data,
            **self.converters
            )
    
      def __len__(self):
        return len(self._data)
    
      def __eq__(self, other):
        return self._data == other._data
    
      def __iter__(self):
        return iter([self.converters['raw2new'](item) for item in self._data])
    

    现在,DataUI 必须看起来像这样:

    class DataUI(object):
      def __init__(self, list_of_ones):
        for one in list_of_ones:
          assert type(one) == One
        self.list_of_ones = list_of_ones
        self.list_of_twos = ListView(
            self.list_of_ones,
            Two.newByDTO,
            Two.getDTO
            )
    

    这样,Two 需要以下方法:

    def getDTO(self):
      return self._dto
    

    整个示例现在如下所示:

    import unittest
    import numpy as np
    
    class ListView(list):
      def __init__(self, raw_list, raw2new, new2raw):
        self._data = raw_list
        self.converters = {'raw2new': raw2new,
            'new2raw': new2raw}
    
      def __repr__(self):
        repr_list = [self.converters['raw2new'](item) for item in self._data]
        repr_str = "["
        for element in repr_list:
          repr_str += element.__repr__() + ",\n "
        repr_str = repr_str[:-3] + "]"
        return repr_str
    
      def append(self, item):
        self._data.append(self.converters['new2raw'](item))
    
      def pop(self, index):
        self._data.pop(index)
    
      def __getitem__(self, index):
        return self.converters['raw2new'](self._data[index])
    
      def __setitem__(self, key, value):
        self._data.__setitem__(key, self.converters['new2raw'](value))
    
      def __delitem__(self, key):
        return self._data.__delitem__(key)
    
      def __getslice__(self, i, j):
        return ListView(self._data.__getslice__(i,j), **self.converters)
    
      def __contains__(self, item):
        return self._data.__contains__(self.converters['new2raw'](item))
    
      def __add__(self, other_list_view):
        assert self.converters == other_list_view.converters
        return ListView(
            self._data + other_list_view._data,
            **self.converters
            )
    
      def __len__(self):
        return len(self._data)
    
      def __iter__(self):
        return iter([self.converters['raw2new'](item) for item in self._data])
    
      def __eq__(self, other):
        return self._data == other._data
    
    
    class One(object):
      """
      Data Transfere Object (DTO)
      """
      def __init__(self, name, data):
        assert type(name) == str
        assert type(data) == str
        self.name = name
        self.data = data
    
      def __repr__(self):
        return "%s(%r, %r)" %(self.__class__.__name__, self.name, self.data)
    
    
    class Two(np.ndarray):
      _DTO = One
      def __new__(cls, name, data):
        dto = cls._DTO(name, data)
        return cls.newByDTO(dto)
    
      @classmethod
      def newByDTO(cls, dto):
        obj = np.fromstring(dto.data, dtype="float", sep=',').view(cls)
        obj.setflags(write=False) # Immutable
        obj._dto = dto
        return obj
    
      @property
      def name(self):
        return self._dto.name
    
      def getDTO(self):
        return self._dto
    
    
    class DataUI(object):
      def __init__(self, list_of_ones):
        for one in list_of_ones:
          assert type(one) == One
        self.list_of_ones = list_of_ones
        self.list_of_twos = ListView(
            self.list_of_ones,
            Two.newByDTO,
            Two.getDTO
            )
    
    
    class TestListView(unittest.TestCase):
      def testProperties(self):
        o1 = One('first object', "1, 3.0, 7, 8,1")
        o2 = One('second object', "3.7, 8, 10")
        my_data = DataUI ([o1, o2])
    
        t1 = Two('third object', "4.8, 8.2, 10.3")
        t2 = Two('forth object', "33, 1.8, 1.0")
        # append:
        my_data.list_of_twos.append(t1)
        # __getitem__:
        np.testing.assert_array_equal(my_data.list_of_twos[2], t1)
        # __add__:
        np.testing.assert_array_equal(
            (my_data.list_of_twos + my_data.list_of_twos)[5], t1)
        # __getslice__:
        np.testing.assert_array_equal(
            my_data.list_of_twos[1:],
            my_data.list_of_twos[1:2] + my_data.list_of_twos[2:]
            )
        # __contains__:
        self.assertEqual(my_data.list_of_twos.__contains__(t1), True)
        # __setitem__:
        my_data.list_of_twos.__setitem__(1, t1),
        np.testing.assert_array_equal(my_data.list_of_twos[1], t1)
        # __delitem__:
        l1 = len(my_data.list_of_twos)
        my_data.list_of_twos.__delitem__(1)
        l2 = len(my_data.list_of_twos)
        self.assertEqual(l1 - 1, l2)
        # __iter__:
        my_data_2 = DataUI ([])
        for two in my_data.list_of_twos:
          my_data_2.list_of_twos.append(two)
    
    
    if __name__ == '__main__':
      unittest.main()
    

    【讨论】:

      猜你喜欢
      • 2013-05-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-01
      • 2020-06-02
      相关资源
      最近更新 更多