Considere la siguiente clase:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
Mis compañeros de trabajo tienden a definirlo así:
class Person:
name = None
age = None
def __init__(self, name, age):
self.name = name
self.age = age
La razón principal de esto es que su editor de elección muestra las propiedades para el autocompletado.
Personalmente, no me gusta el último, porque no tiene sentido que una clase tenga esas propiedades establecidas None
.
¿Cuál sería una mejor práctica y por qué razones?
__init__
ya proporciona autocompletado, etc. Además, el usoNone
evita que el IDE infiera un mejor tipo para el atributo, por lo que es mejor usar un valor predeterminado sensible en su lugar (cuando posible).typing
módulo, que le permiten proporcionar pistas al IDE y la interfaz, si ese tipo de cosas le hacen cosquillas ...self
. Incluso si estuvieran asignadosself.name
oself.age
no__init__
, no aparecerían en la instanciaself
, solo aparecerían en la clasePerson
.Respuestas:
Llamo a esta última mala práctica bajo la regla "esto no hace lo que crees que hace".
La posición de su compañero de trabajo puede reescribirse como: "Voy a crear un montón de variables cuasi-globales estáticas de clase a las que nunca se accede, pero que ocupan espacio en las tablas de espacios de nombres de varias clases (
__dict__
), solo para hacer que mi IDE funcione alguna cosa."fuente
-OO
, para aquellos pocos que lo necesitan.1. Haz tu código fácil de entender
El código se lee con mucha más frecuencia que lo escrito. Facilite la tarea de su mantenedor de código (también puede ser usted mismo el próximo año).
No conozco ninguna regla estricta, pero prefiero que cualquier estado de instancia futuro se declare claramente. Chocar con un ya
AttributeError
es bastante malo. No ver claramente el ciclo de vida de un atributo de instancia es peor. La cantidad de gimnasia mental requerida para restaurar las posibles secuencias de llamadas que conducen al atributo asignado puede convertirse fácilmente en algo no trivial y provocar errores.Por lo tanto, generalmente no solo defino todo en el constructor, sino que también me esfuerzo por mantener el número de atributos mutables al mínimo.
2. No mezcle miembros de nivel de clase y nivel de instancia
Cualquier cosa que defina dentro de la
class
declaración pertenece a la clase y es compartida por todas las instancias de la clase. Por ejemplo, cuando define una función dentro de una clase, se convierte en un método que es el mismo para todas las instancias. Lo mismo se aplica a los miembros de datos. Esto es totalmente diferente a los atributos de instancia que usualmente define__init__
.Los miembros de datos de nivel de clase son más útiles como constantes:
fuente
self
y no necesitanself
ser pasados, mientras que los métodos a nivel de clase no están vinculados y son funciones simples como se ve en eldef
momento, y aceptan una instancia como primer argumento. Entonces estas son cosas diferentes.AttributeError
es una buena señal de que hay un error. De lo contrario, se tragaría el Ninguno y obtendría resultados sin sentido. Especialmente importante en el caso en cuestión, donde los atributos se definen en el__init__
, por lo que un atributo faltante (pero existente a nivel de clase) solo podría ser causado por una herencia defectuosa.None
y este valor no tiene sentido en el momento de la construcción de la instancia, tiene un problema en su arquitectura y tiene que repensar el ciclo de vida del valor del atributo o su valor inicial. Tenga en cuenta que al definir sus atributos temprano, puede detectar dicho problema incluso antes de escribir el resto de la clase, y mucho menos ejecutar el código.Personalmente defino los miembros en el método __ init __ (). Nunca pensé en definirlos en la parte de la clase. Pero lo que siempre hago: inicio a todos los miembros en el método __ init__, incluso aquellos que no son necesarios en el método __ init__.
Ejemplo:
Creo que es importante definir a todos los miembros en un solo lugar. Hace que el código sea más legible. Ya sea dentro de __ init __ () o afuera, no es tan importante. Pero es importante que un equipo se comprometa con más o menos el mismo estilo de codificación.
Ah, y puede notar que alguna vez agrego el prefijo "_" a las variables miembro.
fuente
_
: ¡Para indicar que es privado! (¿Por qué tantas personas en este hilo confunden o confunden Python con otros idiomas?)Esta es una mala práctica. No necesita esos valores, desordenan el código y pueden causar errores.
Considerar:
fuente
Iré con "un poco como docstrings, entonces" y declararé esto inofensivo, siempre y cuando sea siempre
None
, o un rango estrecho de otros valores, todos inmutables.Apesta a atavismo y apego excesivo a lenguajes estáticamente escritos. Y no sirve como código. Pero tiene un propósito menor que queda, en la documentación.
Documenta cuáles son los nombres esperados, por lo que si combino el código con alguien y uno de nosotros tiene 'nombre de usuario' y el otro nombre de usuario, hay una pista para los humanos de que nos hemos separado y no estamos usando las mismas variables.
Forzar la inicialización completa como política logra lo mismo de una manera más pitónica, pero si hay un código real en el
__init__
, esto proporciona un lugar más claro para documentar las variables en uso.Obviamente, el GRAN problema aquí es que tienta a la gente a inicializar con valores distintos a
None
, que pueden ser malos:deja un rastro global y no crea una instancia para x.
Pero eso es más una peculiaridad en Python en general que en esta práctica, y ya deberíamos estar paranoicos al respecto.
fuente