una tuplas por misma metodos llenar listas lista elementos comprensión comparar comando agregar python mutable namedtuple

tuplas - ¿Existencia de tupla llamada mutable en Python?



metodos de listas en python (10)

Como una alternativa muy pitónica para esta tarea, desde Python-3.7, puede usar el módulo de dataclasses que no solo se comporta como un NamedTuple mutable porque usan definiciones de clase normales sino que también admiten otras características de clases.

Desde PEP-0557:

Aunque utilizan un mecanismo muy diferente, las clases de datos pueden considerarse como "tuplas con mutable nombre con valores predeterminados". Debido a que las Clases de datos usan una sintaxis de definición de clase normal, puede usar herencia, metaclases, cadenas de documentos, métodos definidos por el usuario, fábricas de clases y otras características de clase de Python.

Se proporciona un decorador de clase que inspecciona una definición de clase para variables con anotaciones de tipo como se define en PEP 526 , "Sintaxis para anotaciones variables". En este documento, tales variables se llaman campos. Usando estos campos, el decorador agrega definiciones de métodos generados a la clase para admitir la inicialización de instancias, una repr, métodos de comparación y, opcionalmente, otros métodos como se describe en la sección Specification . Dicha clase se llama Clase de datos, pero en realidad no tiene nada de especial: el decorador agrega métodos generados a la clase y devuelve la misma clase que se le dio.

Esta característica se introduce en PEP-0557 que puede leer sobre ella en más detalles en el enlace de documentación proporcionado.

Ejemplo:

In [20]: from dataclasses import dataclass In [21]: @dataclass ...: class InventoryItem: ...: ''''''Class for keeping track of an item in inventory.'''''' ...: name: str ...: unit_price: float ...: quantity_on_hand: int = 0 ...: ...: def total_cost(self) -> float: ...: return self.unit_price * self.quantity_on_hand ...:

Manifestación:

In [23]: II = InventoryItem(''bisc'', 2000) In [24]: II Out[24]: InventoryItem(name=''bisc'', unit_price=2000, quantity_on_hand=0) In [25]: II.name = ''choco'' In [26]: II.name Out[26]: ''choco'' In [27]: In [27]: II.unit_price *= 3 In [28]: II.unit_price Out[28]: 6000 In [29]: II Out[29]: InventoryItem(name=''choco'', unit_price=6000, quantity_on_hand=0)

¿Alguien puede modificar namedtuple o proporcionar una clase alternativa para que funcione para objetos mutables?

Principalmente por legibilidad, me gustaría algo similar a namedtuple que haga esto:

from Camelot import namedgroup Point = namedgroup(''Point'', [''x'', ''y'']) p = Point(0, 0) p.x = 10 >>> p Point(x=10, y=0) >>> p.x *= 10 Point(x=100, y=0)

Debe ser posible encurtir el objeto resultante. Y según las características de la tupla nombrada, el orden de la salida cuando se representa debe coincidir con el orden de la lista de parámetros al construir el objeto.


Hay una alternativa mutable a collections.namedtuple - recordclass .

Tiene la misma API y huella de memoria que namedtuple y admite asignaciones (también debería ser más rápido). Por ejemplo:

from recordclass import recordclass Point = recordclass(''Point'', ''x y'') >>> p = Point(1, 2) >>> p Point(x=1, y=2) >>> print(p.x, p.y) 1 2 >>> p.x += 2; p.y += 3; print(p) Point(x=3, y=5)

Para python 3.6 y más alto recordclass (desde 0.5) admite typehints:

from recordclass import recordclass, RecordClass class Point(RecordClass): x: int y: int >>> Point.__annotations__ {''x'':int, ''y'':int} >>> p = Point(1, 2) >>> p Point(x=1, y=2) >>> print(p.x, p.y) 1 2 >>> p.x += 2; p.y += 3; print(p) Point(x=3, y=5)

Hay un example más completo (también incluye comparaciones de rendimiento).

Desde 0.9, la biblioteca recordclass proporciona otra variante: la función de fábrica recordclass.structclass . Puede producir clases, cuyas instancias ocupan menos memoria que las instancias basadas en __slots__ . Esto puede ser importante para las instancias con valores de atributo, que no tiene la intención de tener ciclos de referencia. Puede ayudar a reducir el uso de memoria si necesita crear millones de instancias. Aquí hay un example ilustrativo.


Implementemos esto con la creación dinámica de tipos:

import copy def namedgroup(typename, fieldnames): def init(self, **kwargs): attrs = {k: None for k in self._attrs_} for k in kwargs: if k in self._attrs_: attrs[k] = kwargs[k] else: raise AttributeError(''Invalid Field'') self.__dict__.update(attrs) def getattribute(self, attr): if attr.startswith("_") or attr in self._attrs_: return object.__getattribute__(self, attr) else: raise AttributeError(''Invalid Field'') def setattr(self, attr, value): if attr in self._attrs_: object.__setattr__(self, attr, value) else: raise AttributeError(''Invalid Field'') def rep(self): d = ["{}={}".format(v,self.__dict__[v]) for v in self._attrs_] return self._typename_ + ''('' + '', ''.join(d) + '')'' def iterate(self): for x in self._attrs_: yield self.__dict__[x] raise StopIteration() def setitem(self, *args, **kwargs): return self.__dict__.__setitem__(*args, **kwargs) def getitem(self, *args, **kwargs): return self.__dict__.__getitem__(*args, **kwargs) attrs = {"__init__": init, "__setattr__": setattr, "__getattribute__": getattribute, "_attrs_": copy.deepcopy(fieldnames), "_typename_": str(typename), "__str__": rep, "__repr__": rep, "__len__": lambda self: len(fieldnames), "__iter__": iterate, "__setitem__": setitem, "__getitem__": getitem, } return type(typename, (object,), attrs)

Esto verifica los atributos para ver si son válidos antes de permitir que la operación continúe.

Entonces, ¿es esto en escabeche? Sí si (y solo si) haces lo siguiente:

>>> import pickle >>> Point = namedgroup("Point", ["x", "y"]) >>> p = Point(x=100, y=200) >>> p2 = pickle.loads(pickle.dumps(p)) >>> p2.x 100 >>> p2.y 200 >>> id(p) != id(p2) True

La definición debe estar en su espacio de nombres y debe existir el tiempo suficiente para que Pickle la encuentre. Entonces, si define que esto está en su paquete, debería funcionar.

Point = namedgroup("Point", ["x", "y"])

Pickle fallará si hace lo siguiente o hace que la definición sea temporal (queda fuera del alcance cuando finaliza la función, por ejemplo):

some_point = namedgroup("Point", ["x", "y"])

Y sí, conserva el orden de los campos enumerados en la creación del tipo.


La siguiente es una buena solución para Python 3: una clase mínima que usa __slots__ y la clase base abstracta de Sequence ; no hace una detección de errores sofisticada o algo así, pero funciona y se comporta principalmente como una tupla mutable (a excepción de la comprobación de tipos).

from collections import Sequence class NamedMutableSequence(Sequence): __slots__ = () def __init__(self, *a, **kw): slots = self.__slots__ for k in slots: setattr(self, k, kw.get(k)) if a: for k, v in zip(slots, a): setattr(self, k, v) def __str__(self): clsname = self.__class__.__name__ values = '', ''.join(''%s=%r'' % (k, getattr(self, k)) for k in self.__slots__) return ''%s(%s)'' % (clsname, values) __repr__ = __str__ def __getitem__(self, item): return getattr(self, self.__slots__[item]) def __setitem__(self, item, value): return setattr(self, self.__slots__[item], value) def __len__(self): return len(self.__slots__) class Point(NamedMutableSequence): __slots__ = (''x'', ''y'')

Ejemplo:

>>> p = Point(0, 0) >>> p.x = 10 >>> p Point(x=10, y=0) >>> p.x *= 10 >>> p Point(x=100, y=0)

Si lo desea, también puede tener un método para crear la clase (aunque usar una clase explícita es más transparente):

def namedgroup(name, members): if isinstance(members, str): members = members.split() members = tuple(members) return type(name, (NamedMutableSequence,), {''__slots__'': members})

Ejemplo:

>>> Point = namedgroup(''Point'', [''x'', ''y'']) >>> Point(6, 42) Point(x=6, y=42)

En Python 2, debe ajustarlo ligeramente; si hereda de Sequence , la clase tendrá un __dict__ y los __slots__ dejarán de funcionar.

La solución en Python 2 es no heredar de Sequence , sino object . Si se isinstance(Point, Sequence) == True , debe registrar NamedMutableSequence como una clase base para Sequence :

Sequence.register(NamedMutableSequence)


Las tuplas son, por definición, inmutables.

Sin embargo, puede hacer una subclase de diccionario donde puede acceder a los atributos con notación de puntos;

In [1]: %cpaste Pasting code; enter ''--'' alone on the line to stop or use Ctrl-D. :class AttrDict(dict): : : def __getattr__(self, name): : return self[name] : : def __setattr__(self, name, value): : self[name] = value :-- In [2]: test = AttrDict() In [3]: test.a = 1 In [4]: test.b = True In [5]: test Out[5]: {''a'': 1, ''b'': True}


Parece que la respuesta a esta pregunta es no.

A continuación está bastante cerca, pero no es técnicamente mutable. Esto está creando una nueva instancia namedtuple() con un valor x actualizado:

Point = namedtuple(''Point'', [''x'', ''y'']) p = Point(0, 0) p = p._replace(x=10)

Por otro lado, puede crear una clase simple usando __slots__ que debería funcionar bien para actualizar frecuentemente los atributos de instancia de clase:

class Point: __slots__ = [''x'', ''y''] def __init__(self, x, y): self.x = x self.y = y

Para agregar a esta respuesta, creo que __slots__ es un buen uso aquí porque es eficiente en memoria cuando creas muchas instancias de clase. El único inconveniente es que no puede crear nuevos atributos de clase.

Aquí hay un hilo relevante que ilustra la eficiencia de la memoria, Diccionario vs Objeto, ¿cuál es más eficiente y por qué?

El contenido citado en la respuesta de este hilo es una explicación muy sucinta de por qué __slots__ es más eficiente en memoria - Slots de Python


Si desea un comportamiento similar al de namedtuples pero mutable intente namedlist

Tenga en cuenta que para ser mutable no puede ser una tupla.


Siempre que el rendimiento sea de poca importancia, uno podría usar un truco tonto como:

from collection import namedtuple Point = namedtuple(''Point'', ''x y z'') mutable_z = Point(1,2,[3])


types.SimpleNamespace se introdujo en Python 3.3 y admite los requisitos solicitados.

from types import SimpleNamespace t = SimpleNamespace(foo=''bar'') t.ham = ''spam'' print(t) namespace(foo=''bar'', ham=''spam'') print(t.foo) ''bar'' import pickle with open(''/tmp/pickle'', ''wb'') as f: pickle.dump(t, f)


La última lista namedlist 1.7 pasa todas sus pruebas con Python 2.7 y Python 3.5 a partir del 11 de enero de 2016. Es una implementación pura de Python, mientras que la recordclass es una extensión C. Por supuesto, depende de sus requisitos si una extensión C es preferida o no.

Sus pruebas (pero también vea la nota a continuación):

from __future__ import print_function import pickle import sys from namedlist import namedlist Point = namedlist(''Point'', ''x y'') p = Point(x=1, y=2) print(''1. Mutation of field values'') p.x *= 10 p.y += 10 print(''p: {}, {}/n''.format(p.x, p.y)) print(''2. String'') print(''p: {}/n''.format(p)) print(''3. Representation'') print(repr(p), ''/n'') print(''4. Sizeof'') print(''size of p:'', sys.getsizeof(p), ''/n'') print(''5. Access by name of field'') print(''p: {}, {}/n''.format(p.x, p.y)) print(''6. Access by index'') print(''p: {}, {}/n''.format(p[0], p[1])) print(''7. Iterative unpacking'') x, y = p print(''p: {}, {}/n''.format(x, y)) print(''8. Iteration'') print(''p: {}/n''.format([v for v in p])) print(''9. Ordered Dict'') print(''p: {}/n''.format(p._asdict())) print(''10. Inplace replacement (update?)'') p._update(x=100, y=200) print(''p: {}/n''.format(p)) print(''11. Pickle and Unpickle'') pickled = pickle.dumps(p) unpickled = pickle.loads(pickled) assert p == unpickled print(''Pickled successfully/n'') print(''12. Fields/n'') print(''p: {}/n''.format(p._fields)) print(''13. Slots'') print(''p: {}/n''.format(p.__slots__))

Salida en Python 2.7

1. Mutation of field values p: 10, 12 2. String p: Point(x=10, y=12) 3. Representation Point(x=10, y=12) 4. Sizeof size of p: 64 5. Access by name of field p: 10, 12 6. Access by index p: 10, 12 7. Iterative unpacking p: 10, 12 8. Iteration p: [10, 12] 9. Ordered Dict p: OrderedDict([(''x'', 10), (''y'', 12)]) 10. Inplace replacement (update?) p: Point(x=100, y=200) 11. Pickle and Unpickle Pickled successfully 12. Fields p: (''x'', ''y'') 13. Slots p: (''x'', ''y'')

La única diferencia con Python 3.5 es que la lista namedlist ha vuelto más pequeña, el tamaño es 56 (Python 2.7 informa 64).

Tenga en cuenta que he cambiado su prueba 10 para el reemplazo en el lugar. El namedlist tiene un método _replace() que hace una copia superficial, y eso tiene mucho sentido para mí porque el namedtuple en la biblioteca estándar se comporta de la misma manera. Cambiar la semántica del método _replace() sería confuso. En mi opinión, el método _update() debería usarse para las actualizaciones _update() . ¿O tal vez no entendí la intención de tu prueba 10?