python - ordereddict - No se puede establecer el atributo para las subclases de namedtuple
named tuple c# (4)
Parece que this o this son temas relacionados, pero aún no he descubierto las cosas :)
Estoy tratando de crear una subclase de namedtuple
de namedtuple
y proporcionar inicializadores diferentes para poder construir objetos de diferentes maneras. Por ejemplo:
>>> from collections import namedtuple
>>> class C(namedtuple("C", "x, y")) :
... __slots__ = ()
... def __init__(self, obj) : # Initialize a C instance by copying values from obj
... self.x = obj.a
... self.y = obj.b
... def __init__(self, x, y) : # Initialize a C instance from the parameters
... self.x = x
... self.y = y
Sin embargo, eso no funciona:
>>> c = C(1, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in __init__
AttributeError: can''t set attribute
Después de hurgarme (por ejemplo, ver this hilo) intenté usar constructores en lugar de inicializadores:
>>> from collections import namedtuple
>>> class C(namedtuple("C", "x, y")) :
... __slots__ = ()
... def __new__(cls, obj) :
... self = super(C, cls).__new__(cls, obj.a, obj.b)
... def __new__(cls, x, y) :
... self = super(C, cls).__new__(cls, x, y)
que parecía construir un objeto pero luego no puedo leer sus atributos:
>>> c = C(1,2)
>>> c.x, c.y
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: ''NoneType'' object has no attribute ''x''
¿A dónde me voy mal aquí? ¿Cómo puedo crear una subclase con múltiples constructores o inicializadores?
Dos cosas: una, realmente no estás obteniendo mucho de tu nombre aquí, por lo que puedo decir. Así que tal vez debería cambiar a una clase normal. Además, no puede sobrecargar el
Segundo, otras posibilidades que podrían ayudarte con tu problema:
Patrón de diseño de fábrica : en lugar de colocar los diferentes parámetros en el constructor, tenga una clase que tome diferentes tipos de parámetros y llame al constructor con los argumentos apropiados, fuera del objeto. recordtype : un objeto mutable con nombre, que permite valores predeterminados pero también le permitiría escribir su subclase de la manera que originalmente quería. bunch : no es exactamente una tupla con nombre, pero le permite crear objetos un tanto arbitrarios.
Hay una solución alternativa para cambiar el atributo de un grupo con nombre.
import collections
def updateTuple(NamedTuple,nameOfNamedTuple):
## Convert namedtuple to an ordered dictionary, which can be updated
NamedTuple_asdict = NamedTuple._asdict()
## Make changes to the required named attributes
NamedTuple_asdict[''path'']= ''www.google.com''
## reconstruct the namedtuple using the updated ordered dictionary
updated_NamedTuple = collections.namedtuple(nameOfNamedTuple, NamedTuple_asdict.keys())(**NamedTuple_asdict)
return updated_NamedTuple
Tuple = collections.namedtuple("Tuple", "path")
NamedTuple = Tuple(path=''www.yahoo.com'')
NamedTuple = updateTuple(NamedTuple, "Tuple")
Las tuplas nombradas son inmutables, por lo que no puede manipularlas en el inicializador __init__
. Su única opción es anular el método __new__
:
class C(namedtuple(''C'', ''x, y'')):
__slots__ = ()
def __new__(cls, obj):
return super(C, cls).__new__(cls, obj.x, obj.y)
Tenga en cuenta que debido a que __new__
es un método de fábrica para las nuevas instancias, debe devolver la instancia recién creada. Si no usa return
en el método __new__
, el valor de retorno predeterminado es None
, lo que le da su error.
Demostración con un objeto con atributos x
e y
:
>>> class C(namedtuple(''C'', ''x, y'')):
... __slots__ = ()
... def __new__(cls, obj):
... return super(C, cls).__new__(cls, obj.x, obj.y)
...
>>> O.x, O.y
(10, 20)
>>> C(O)
C(x=10, y=20)
Python no admite la sobrecarga de métodos; por lo general, usa argumentos de palabras clave opcionales o métodos de clase adicionales como métodos de fábrica.
El módulo datetime
, por ejemplo, tiene varios métodos de fábrica para permitirle crear objetos que no se ajusten al constructor estándar. datetime.datetime.fromtimestamp()
crea una instancia de datetime.datetime
partir de un solo valor numérico, y también lo hace datetime.datetime.fromordinal()
; Excepto que interpretan el número de diferentes maneras.
Si quieres apoyar argumentos variables, haz:
class C(namedtuple(''C'', ''x, y'')):
__slots__ = ()
def __new__(cls, x, y=None):
if y is None:
# assume attributes
x, y = x.x, x.y
return super(C, cls).__new__(cls, x, y)
Aquí, y
es un argumento opcional, el valor predeterminado es None
si no lo proporciona la persona que llama:
>>> C(3, 5):
C(x=3, y=5)
>>> C(O)
C(x=10, y=20)
La alternativa, utilizando un método de clase, sería:
class C(namedtuple(''C'', ''x, y'')):
@classmethod
def from_attributes(cls, obj):
return cls(obj.x, obj.y)
Ahora hay dos métodos de fábrica; uno por defecto y uno nombrado:
>>> C(3, 5):
C(x=3, y=5)
>>> C.from_attributes(O)
C(x=10, y=20)
Te sugiero que uses el método docs.python.org/2/library/…
from collections import namedtuple
C = namedtuple(''C'', ''x, y'')
c = C(x=10, y=20)
# c.x = 30 won''t work
c = c._replace(x=30)