Как я могу изменить в Python тип возврата / ввода списка, который реализован как атрибут класса? - PullRequest
0 голосов
/ 24 января 2012

РЕДАКТИРОВАТЬ (полная перефразировка проблемы, поскольку первоначальная версия (см. «Оригинальная версия», позже) вводит в заблуждение):

Вот настройка: у меня есть объект, который имеетсписок объектов типа <class 'One'>.Я хотел бы получить доступ к этому списку, но лучше работать с объектами типа <class 'Two'>, который является обогащенной версией <class 'One'>.

Background (1):

  • Oneможет быть объектом, который может быть легко сохранен через ORM.ORM будет обрабатывать список в зависимости от модели данных.
  • Two будет объектом, подобным One, но обогащенным многими функциями или способами доступа к нему

. Фон(2):

  • Я пытаюсь решить вопрос, связанный с 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_list через другой список (например, my_data.data_np_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]

Ответы [ 4 ]

0 голосов
/ 24 января 2012

Единственное решение, которое я только что придумал, - это реализовать просмотр списка через класс ListView, который принимает следующие аргументы:

  • raw_list: список One -объектов
  • raw2new: функция, которая преобразует One -объекты в Two -объекты
  • new2raw: функция, которая преобразует Two -объекты в One -объекты

Вот код:

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()
0 голосов
/ 24 января 2012

Нет, вы не можете сделать это легко (или вообще не потерять прирост производительности, который вы можете получить, используя numpy.array).Вам нужны две принципиально разные структуры, отражающие друг друга, это будет означать сохранение двух и перенос любых модификаций между ними;Подклассы list и numpy.array для наблюдения изменений будут единственным способом сделать это.

0 голосов
/ 24 января 2012

Не уверен, что ваш подход правильный.

Получатель недвижимости поможет вам добиться того, что вы делаете.Вот что-то похожее с использованием массивов вместо 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()
0 голосов
/ 24 января 2012

Вы должны использовать свойство

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 использовать непрерывную область памяти.список питонов - это связанный список.Вы не можете иметь и то и другое одновременно, не заплатив за производительность, которая сделает все это бесполезным.Это разные структуры данных.

...