Veo patrones como
def __init__(self, x, y, z):
...
self.x = x
self.y = y
self.z = z
...
con bastante frecuencia, a menudo con muchos más parámetros. ¿Hay una buena manera de evitar este tipo de repetitividad tediosa? ¿Debería la clase heredar de su namedtuple
lugar?
__slots__
embargo , puede usar para este propósito ; es levemente poco fónico (más detallado para obtener ahorros de memoria), pero me gusta en gran medida para evitar el riesgo de auto-vivificar un atributo completamente nuevo si escribo el nombre.ini <shortcut> x, y, z): <shortcut>
y listo.Respuestas:
Editar: si tiene Python 3.7+, solo use clases de datos
Una solución decoradora que mantiene la firma:
fuente
signature
Descargo de responsabilidad: Parece que varias personas están preocupadas por presentar esta solución, por lo que proporcionaré un descargo de responsabilidad muy claro. No deberías usar esta solución. Solo lo proporciono como información, para que sepa que el idioma es capaz de esto. El resto de la respuesta es solo mostrar capacidades de lenguaje, no respaldar su uso de esta manera.
Realmente no hay nada malo en copiar explícitamente los parámetros en los atributos. Si tiene demasiados parámetros en el ctor, a veces se considera un olor a código y tal vez debería agrupar estos parámetros en menos objetos. Otras veces, es necesario y no tiene nada de malo. De todos modos, hacerlo explícitamente es el camino a seguir.
Sin embargo, dado que usted pregunta CÓMO se puede hacer (y no si se debe hacer), entonces una solución es esta:
fuente
self.__dict__.update(kwargs)
podría ser marginalmente más pitónicoA.__init__
realmente esperan, y no hay verificación de errores para nombres de argumentos mal escritos.kwargs
te deja abierto al equivalente de un ataque de inyección SQL. Si su objeto tiene un método llamadomy_method
y le pasa un argumentomy_method
al constructor, entoncesupdate()
el diccionario, simplemente sobrescribió el método.Como otros han mencionado, la repetición no es mala, pero en algunos casos una tupla con nombre puede ser una gran opción para este tipo de problema. Esto evita el uso de locales () o kwargs, que generalmente son una mala idea.
He encontrado un uso limitado para él, pero puede heredar una tupla nombrada como con cualquier otro objeto (ejemplo continuado):
fuente
properties
método puede escribirse como justoreturn tuple(self)
, lo que es más fácil de mantener si en el futuro se agregan más campos a la definición de tupla nombrada.XYZ = namedtuple("XYZ", "x y z")
funciona igual de bien.namedtuple
s para este propósito exacto, especialmente en el código matemático donde una función puede estar altamente parametrizada y tener un montón de coeficientes que solo tienen sentido juntos.namedtuple
es que son de solo lectura. No puedes hacerabc.x += 1
ni nada de eso.explícito es mejor que implícito ... así que seguro que podría hacerlo más conciso:
La mejor pregunta es ¿debería?
... Dicho esto, si desea una tupla con nombre, recomendaría usar una tupla con nombre (recuerde que las tuplas tienen ciertas condiciones asociadas) ... tal vez desee una sentencia ordenada o incluso solo una sentencia ...
fuente
if k != "self":
podría cambiarse aif v is not self:
prueba de identidad barata en lugar de comparación de cadenas. Supongo que técnicamente__init__
podría llamarse por segunda vez después de la construcción y pasarself
como un argumento posterior, pero realmente no quiero pensar qué tipo de monstruo haría eso. :-)locals
:set_fields_from_locals(locals())
. Entonces ya no es más que las soluciones basadas en decoradores más mágicos.Para ampliar
gruszczy
la respuesta de s, he usado un patrón como:Me gusta este método porque:
super().__init(...)
)X.__init__
Antes de Python 3.6, esto no proporciona control sobre el orden en que se establecen los atributos, lo que podría ser un problema si algunos atributos son propiedades con setters que acceden a otros atributos.
Probablemente podría mejorarse un poco, pero soy el único usuario de mi propio código, por lo que no me preocupa ninguna forma de desinfección de entrada. Quizás un
AttributeError
sería más apropiado.fuente
También puedes hacer:
Por supuesto, tendría que importar el
inspect
módulo.fuente
Esta es una solución sin ninguna importación adicional.
Función auxiliar
Una pequeña función auxiliar lo hace más conveniente y reutilizable:
Solicitud
Necesitas llamarlo con
locals()
:Prueba
Salida:
Sin cambiar
locals()
Si no te gusta cambiar
locals()
usa esta versión:fuente
locals()
no debe modificarse (puede afectar al intérprete, en su caso, eliminarloself
del alcance de la función de llamada)self
está disponible en__init__
.Una biblioteca interesante que maneja esto (y evita muchas otras repeticiones) es attrs . Su ejemplo, por ejemplo, podría reducirse a esto (suponga que se llama a la clase
MyClass
):Ya ni siquiera necesita un
__init__
método, a menos que también haga otras cosas. Aquí hay una buena introducción de Glyph Lefkowitz .fuente
attr
redundante la funcionalidad dedataclasses
?Mi 0.02 $. Está muy cerca de la respuesta de Joran Beasley, pero más elegante:
Además, la respuesta de Mike Müller (la mejor para mi gusto) se puede reducir con esta técnica:
Y la simple llamada
auto_init(locals())
de tu__init__
fuente
locals()
no debe modificarse (comportamiento indefinido)Es una forma natural de hacer cosas en Python. No intentes inventar algo más inteligente, conducirá a un código demasiado inteligente que nadie en tu equipo entenderá. Si quieres ser un jugador de equipo y luego seguir escribiéndolo de esta manera.
fuente
Python 3.7 en adelante
En Python 3.7, puede (ab) usar el
dataclass
decorador, disponible desde eldataclasses
módulo. De la documentación:Si su clase es grande y compleja, puede ser inapropiado usar a
dataclass
. Escribo esto el día del lanzamiento de Python 3.7.0, por lo que los patrones de uso aún no están bien establecidos.fuente