how to create namedtuple in python
¿Agregando docstrings a namedtuples? (9)
¿Es posible agregar una cadena de documentación a una tilde nombrada de una manera fácil?
Lo intenté
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
"""
A point in 2D space
"""
# Yet another test
"""
A(nother) point in 2D space
"""
Point2 = namedtuple("Point2", ["x", "y"])
print Point.__doc__ # -> "Point(x, y)"
print Point2.__doc__ # -> "Point2(x, y)"
pero eso no lo corta. ¿Es posible hacerlo de alguna otra manera?
¿Es posible agregar una cadena de documentación a una tilde nombrada de una manera fácil?
Python 3
En Python 3, puede modificar fácilmente el documento en su ruta de nombre:
NT = collections.namedtuple(''NT'', ''foo bar'')
NT.__doc__ = """:param str foo: foo name
:param list bar: List of bars to bar"""
Lo que nos permite ver su intención cuando solicitamos ayuda sobre ellos:
Help on class NT in module __main__:
class NT(builtins.tuple)
| :param str foo: foo name
| :param list bar: List of bars to bar
...
Esto es realmente sencillo en comparación con las dificultades que tenemos para lograr lo mismo en Python 2.
Python 2
En Python 2, necesitarás
- subclase the namedtuple, y
- declarar
__slots__ == ()
Declarar __slots__
es una parte importante que las otras respuestas aquí se pierden .
Si no declara __slots__
, puede agregar atributos mutables ad-hoc a las instancias, introduciendo errores.
class Foo(namedtuple(''Foo'', ''bar'')):
"""no __slots__ = ()!!!"""
Y ahora:
>>> f = Foo(''bar'')
>>> f.bar
''bar''
>>> f.baz = ''what?''
>>> f.__dict__
{''baz'': ''what?''}
Cada instancia creará un __dict__
separado cuando se acceda a __dict__
(la falta de __slots__
no impedirá la funcionalidad, pero la ligereza de la tupla, la inmutabilidad y los atributos declarados son todas características importantes de namedtuples).
También querrás un __repr__
, si quieres que se repita en la línea de comando para darte un objeto equivalente:
NTBase = collections.namedtuple(''NTBase'', ''foo bar'')
class NT(NTBase):
"""
Individual foo bar, a namedtuple
:param str foo: foo name
:param list bar: List of bars to bar
"""
__slots__ = ()
un __repr__
como este es necesario si creas la base namedtuple con un nombre diferente (como hicimos anteriormente con el argumento string de nombre, ''NTBase''
):
def __repr__(self):
return ''NT(foo={0}, bar={1})''.format(
repr(self.foo), repr(self.bar))
Para probar el repr, crea una instancia, luego prueba la igualdad de un pase a eval(repr(instance))
nt = NT(''foo'', ''bar'')
assert eval(repr(nt)) == nt
Ejemplo de la documentación
Los documentos también dan un ejemplo, con respecto a __slots__
- Le agrego mi propia docstring:
class Point(namedtuple(''Point'', ''x y'')): """Docstring added here, not in original""" __slots__ = () @property def hypot(self): return (self.x ** 2 + self.y ** 2) ** 0.5 def __str__(self): return ''Point: x=%6.3f y=%6.3f hypot=%6.3f'' % (self.x, self.y, self.hypot)
...
La subclase que se muestra arriba establece
__slots__
a una tupla vacía. Esto ayuda a mantener bajos los requisitos de memoria al evitar la creación de diccionarios de instancia.
Esto demuestra el uso en el lugar (como sugiere otra respuesta aquí), pero tenga en cuenta que el uso in situ puede ser confuso cuando mira el orden de resolución del método, si está depurando, por lo que originalmente sugerí usar Base
como sufijo para la base namedtuple:
>>> Point.mro()
[<class ''__main__.Point''>, <class ''__main__.Point''>, <type ''tuple''>, <type ''object''>]
# ^^^^^---------------------^^^^^-- same names!
Para evitar la creación de un __dict__
cuando se subclasifica de una clase que lo usa, también debe declararlo en la subclase. Consulte también esta respuesta para obtener más advertencias sobre el uso de __slots__
.
Desde Python 3.5, las cadenas de namedtuple
para objetos namedtuple
se pueden actualizar.
De lo whatsnew :
Point = namedtuple(''Point'', [''x'', ''y'']) Point.__doc__ += '': Cartesian coodinate'' Point.x.__doc__ = ''abscissa'' Point.y.__doc__ = ''ordinate''
En Python 3, no se necesita envoltorio, ya que los atributos de los tipos __doc__
se pueden escribir.
from collections import namedtuple
Point = namedtuple(''Point'', ''x y'')
Point.__doc__ = ''''''/
A 2-dimensional coordinate
x - the abscissa
y - the ordinate''''''
Esto se corresponde estrechamente con una definición de clase estándar, donde la docstring sigue al encabezado.
class Point():
''''''A 2-dimensional coordinate
x - the abscissa
y - the ordinate''''''
<class code>
Esto no funciona en Python 2.
AttributeError: attribute ''__doc__'' of ''type'' objects is not writable
.
En Python 3.6+ puedes usar:
class Point(NamedTuple):
"""
A point in 2D space
"""
x: float
y: float
No es necesario utilizar una clase contenedora tal como lo sugiere la respuesta aceptada. Simplemente agrega literalmente una docstring:
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
Point.__doc__="A point in 2D space"
Esto da como resultado: (ejemplo usando ipython3
):
In [1]: Point?
Type: type
String Form:<class ''__main__.Point''>
Docstring: A point in 2D space
In [2]:
Voilà!
No, solo puede agregar cadenas de documentos a módulos, clases y funciones (incluidos los métodos)
Puede inventar su propia versión de la función de fábrica de títulos nombrados por Raymond Hettinger y agregar un argumento de docstring
opcional. Sin embargo, sería más fácil, y podría decirse mejor, definir su propia función de fábrica utilizando la misma técnica básica que en la receta. De cualquier manera, terminarás con algo reutilizable.
from collections import namedtuple
def my_namedtuple(typename, field_names, verbose=False,
rename=False, docstring=''''):
''''''Returns a new subclass of namedtuple with the supplied
docstring appended to the default one.
>>> Point = my_namedtuple(''Point'', ''x, y'', docstring=''A point in 2D space'')
>>> print Point.__doc__
Point(x, y): A point in 2D space
''''''
# create a base class and concatenate its docstring and the one passed
_base = namedtuple(typename, field_names, verbose, rename)
_docstring = ''''.join([_base.__doc__, '': '', docstring])
# fill in template to create a no-op subclass with the combined docstring
template = ''''''class subclass(_base):
%(_docstring)r
pass/n'''''' % locals()
# execute code string in a temporary namespace
namespace = dict(_base=_base, _docstring=_docstring)
try:
exec template in namespace
except SyntaxError, e:
raise SyntaxError(e.message + '':/n'' + template)
return namespace[''subclass''] # subclass object created
Puede lograrlo creando una clase contenedora simple y vacía alrededor del valor devuelto desde namedtuple
. Contenido de un archivo que creé ( nt.py
):
from collections import namedtuple
Point_ = namedtuple("Point", ["x", "y"])
class Point(Point_):
""" A point in 2d space """
pass
Luego, en el REPL de Python:
>>> print nt.Point.__doc__
A point in 2d space
O podrías hacer:
>>> help(nt.Point) # which outputs...
Help on class Point in module nt: class Point(Point) | A point in 2d space | | Method resolution order: | Point | Point | __builtin__.tuple | __builtin__.object ...
Si no le gusta hacerlo a mano cada vez, es trivial escribir una especie de función de fábrica para hacer esto:
def NamedTupleWithDocstring(docstring, *ntargs):
nt = namedtuple(*ntargs)
class NT(nt):
__doc__ = docstring
return NT
Point3D = NamedTupleWithDocstring("A point in 3d space", "Point3d", ["x", "y", "z"])
p3 = Point3D(1,2,3)
print p3.__doc__
qué salidas:
A point in 3d space
Se encontró con esta vieja pregunta a través de Google mientras se preguntaba lo mismo.
Solo quería señalar que puede arreglarlo aún más llamando a namedtuple () desde la declaración de clase:
from collections import namedtuple
class Point(namedtuple(''Point'', ''x y'')):
"""Here is the docstring."""