¿Cómo puedo cambiar en Python el tipo de retorno / entrada de una lista que se implementa como un atributo de clase?

EDITAR (la reformulación completa del problema como la versión original (ver “versión original”, más adelante) es engañosa):

Aquí está la configuración: Tengo un objeto que tiene una lista de objetos de tipo . Me gustaría acceder a esta lista, pero trabajar con objetos del tipo que es una versión enriquecida de .

Antecedentes (1):

  • One podría ser un objeto que se pueda almacenar fácilmente a través de un ORM. El ORM manejaría la lista dependiendo del modelo de datos
  • Two serían un objeto como One pero enriquecidos por muchas características o por la forma en que se puede acceder

Fondo (2):

  • Intento resolver una pregunta relacionada con SQLAlchemy que hice aquí . Por lo tanto, la respuesta a la pregunta actual también podría ser una solución para el tipo de retorno / entrada cambiante de la pregunta de las listas SQLAlchemy.

Aquí hay un código para la ilustración:

 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]) 

Cómo implementar un list_of_twos que funciona en list_of_ones pero proporciona al usuario una lista con elementos de tipo 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') 

Versión original de la pregunta:

Aquí hay una ilustración del problema:

 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]) 

Me gustaría acceder a my_data.data_list través de otra lista (por ejemplo, my_data.data_np_list ) que maneja los elementos de lista como un tipo diferente (por ejemplo, como 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] 

Debes usar una propiedad

 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 usa un área de memoria continua. Las listas de python son listas enlazadas. No puede tener ambos al mismo tiempo sin pagar un costo de rendimiento que hará que todo sea inútil. Son diferentes estructuras de datos.

No, no puede hacerlo fácilmente (o en absoluto sin perder ninguna ganancia de rendimiento que pueda obtener al usar numpy.array). Si desea dos estructuras fundamentalmente diferentes que se reflejen entre sí, esto significará almacenar las dos y transferir cualquier modificación entre las dos; numpy.array tanto list como numpy.array para observar modificaciones será la única forma de hacerlo.

No estoy seguro de si su enfoque es correcto.

Un gestor de propiedades ayudaría a lograr lo que estás haciendo. Aquí hay algo similar utilizando matrices en lugar de numpy.

He hecho de la matriz (o, en su caso, el tipo de datos numpy) la representación interna, y la conversión a la lista solo se realiza a pedido con un objeto temporal devuelto.

 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() 

Una solución que se me ocurrió fue implementar una Vista de la lista a través de la clase ListView que toma los siguientes argumentos:

  • raw_list : una lista de One -objects
  • raw2new : una función que convierte One -objects en Two -objects
  • new2raw : una función que convierte Two -objects en One -objects

Aquí está el código:

 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]) 

Ahora, DataUI tiene que mirar algo como esto:

 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 ) 

Con eso, Two necesita el siguiente método:

 def getDTO(self): return self._dto 

El ejemplo completo ahora sería como el siguiente:

 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()