¿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 con nombre, el orden de la salida cuando se representa debe coincidir con el orden de la lista de parámetros al construir el objeto.
python
mutable
namedtuple
Alejandro
fuente
fuente
namedtuple
s, parece que no tiene necesidad de poder hacer referencia a los atributos por índice, es decir, entoncesp[0]
yp[1]
serían formas alternativas de referenciax
yy
, respectivamente, ¿correcto?Respuestas:
Existe 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:Para Python 3.6 y superior
recordclass
(desde 0.5), admite sugerencias de tipo:Hay un ejemplo más completo (también incluye comparaciones de desempeño).
Dado que la
recordclass
biblioteca 0.9 proporciona otra variante:recordclass.structclass
función de fábrica. Puede producir clases, cuyas instancias ocupan menos memoria que las__slots__
instancias basadas en. Esto puede ser importante para las instancias con valores de atributo, que no han tenido la intención de tener ciclos de referencia. Puede ayudar a reducir el uso de memoria si necesita crear millones de instancias. A continuación se muestra un ejemplo ilustrativo .fuente
recordclass
es más lento, requiere más memoria y requiere extensiones C en comparación con la receta de Antti Haapala ynamedlist
.recordclass
es una versión mutablecollection.namedtuple
que hereda su api, huella de memoria, pero asignaciones de soporte.namedlist
es en realidad una instancia de la clase Python con ranuras. Es más útil si no necesita un acceso rápido a sus campos por índice.recordclass
ejemplo (Python 3.5.2), es aproximadamente un 2-3% más lento que paranamedlist
namedtuple
una creación de clase simplePoint = namedtuple('Point', 'x y')
, Jedi puede autocompletar atributos, mientras que este no es el casorecordclass
. Si utilizo el código de creación más largo (basado enRecordClass
), entonces Jedi entiende laPoint
clase, pero no su constructor o atributos ... ¿Hay alguna formarecordclass
de trabajar bien con Jedi?types.SimpleNamespace se introdujo en Python 3.3 y es compatible con los requisitos solicitados.
fuente
SimpleNamespace
falla las pruebas 6-10 (acceso por índice, desempaquetado iterativo, iteración, dictado ordenado, reemplazo in situ) y 12, 13 (campos, ranuras). Tenga en cuenta que la documentación (que vinculó en la respuesta) dice específicamente "SimpleNamespace
puede ser útil como reemplazo declass NS: pass
. Sin embargo, para un tipo de registro estructurado, utilicenamedtuple()
en su lugar".SimpleNamespace
crea un objeto, no un constructor de clase, y no puede reemplazar a namedtuple. La comparación de tipos no funcionará y la huella de memoria será mucho mayor.Como una alternativa muy Pythonic para esta tarea, desde Python-3.7, puede usar un
dataclasses
módulo que no solo se comporta como un mutableNamedTuple
porque usan definiciones de clases normales sino que también admiten otras características de clases.Desde PEP-0557:
Esta función se introduce en PEP-0557 y puede leerla con más detalles en el enlace de documentación proporcionado.
Ejemplo:
Manifestación:
fuente
dataclass
falla las pruebas 6-10 (acceso por índice, desempaquetado iterativo, iteración, dictado ordenado, reemplazo in situ) y 12, 13 (campos, ranuras) en Python 3.7 .1.La última lista de nombres 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 de Python pura, mientras que
recordclass
es una extensión de C. Por supuesto, depende de sus requisitos si se prefiere una extensión C o no.Tus pruebas (pero también consulta la nota a continuación):
Salida en Python 2.7
La única diferencia con Python 3.5 es que se
namedlist
ha vuelto más pequeño, el tamaño es 56 (Python 2.7 informa 64).Tenga en cuenta que cambié su prueba 10 para reemplazo en el lugar. El
namedlist
tiene un_replace()
método que hace una copia superficial, y que tiene mucho sentido para mí, porque elnamedtuple
de la biblioteca estándar se comporta de la misma manera. Cambiar la semántica del_replace()
método sería confuso. En mi opinión, el_update()
método debería usarse para actualizaciones in situ. ¿O tal vez no entendí la intención de tu prueba 10?fuente
namedlist
valores de la tienda en la instancia de lista. El caso es quecpython
'slist
en realidad es una matriz dinámica. Por diseño, asigna más memoria de la necesaria para que la mutación de la lista sea más barata.list
y utiliza de forma predeterminada la__slots__
optimización. Cuando medí, el uso de la memoria fue inferior arecordclass
: 96 bytes frente a 104 bytes para seis campos en Python 2.7recorclass
usa más memoria porque es untuple
objeto similar con un tamaño de memoria variable.types.SimpleNamespace
. Desafortunadamente, a pylint no le gusta :-(Parece que la respuesta a esta pregunta es no.
A continuación se muestra bastante cerca, pero técnicamente no es mutable. Esto está creando una nueva
namedtuple()
instancia con un valor x actualizado:Por otro lado, puede crear una clase simple usando
__slots__
que debería funcionar bien para actualizar con frecuencia los atributos de instancia de clase:Para agregar a esta respuesta, creo que
__slots__
es un buen uso aquí porque es eficiente en la memoria cuando crea 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 la memoria: ranuras de Pythonfuente
La siguiente es una buena solución para Python 3: Una clase mínimo usando
__slots__
ySequence
clase base abstracta; no realiza una elegante detección de errores o algo así, pero funciona y se comporta principalmente como una tupla mutable (excepto para la comprobación de tipo).Ejemplo:
Si lo desea, también puede tener un método para crear la clase (aunque usar una clase explícita es más transparente):
Ejemplo:
En Python 2, debe ajustarlo ligeramente; si hereda
Sequence
, la clase tendrá un__dict__
y__slots__
dejará de funcionar.La solución en Python 2 es no heredar de
Sequence
, peroobject
. Siisinstance(Point, Sequence) == True
lo desea, debe registrarNamedMutableSequence
como clase base paraSequence
:fuente
Implementemos esto con la creación dinámica de tipos:
Esto verifica los atributos para ver si son válidos antes de permitir que continúe la operación.
Entonces, ¿es esto encurtido? Sí, si (y solo si) hace lo siguiente:
La definición tiene que estar en su espacio de nombres y debe existir el tiempo suficiente para que pickle la encuentre. Entonces, si define esto para que esté en su paquete, debería funcionar.
Pickle fallará si hace lo siguiente, o hace que la definición sea temporal (sale del alcance cuando la función finaliza, digamos):
Y sí, conserva el orden de los campos enumerados en la creación del tipo.
fuente
__iter__
método confor k in self._attrs_: yield getattr(self, k)
, admitirá el desempaquetado como una tupla.__len__
,__getitem__
y__setiem__
métodos para apoyar conseguir Valus por el índice, al igual quep[0]
. Con estos últimos bits, esta parece la respuesta más completa y correcta (para mí de todos modos).__len__
y__iter__
son buenos.__getitem__
y__setitem__
realmente se puede asignar aself.__dict__.__setitem__
yself.__dict__.__getitem__
Las tuplas son por definición inmutables.
Sin embargo, puede crear una subclase de diccionario en la que pueda acceder a los atributos con notación de puntos;
fuente
Si desea un comportamiento similar al de namedtuples pero mutable, intente namedlist
Tenga en cuenta que para ser mutable no puede ser una tupla.
fuente
Siempre que el rendimiento sea de poca importancia, uno podría usar un truco tonto como:
fuente
z
, debes llamarmutable_z.z.pop(0)
entoncesmutable_z.z.append(new_value)
. Si se equivoca, terminará con más de 1 elemento y su programa se comportará inesperadamente.mutable_z.z[0] = newValue
. De hecho, es un truco, como se dijo.