¿Debo dar a los miembros de mi clase valores predeterminados como este:
class Foo:
num = 1
¿o así?
class Foo:
def __init__(self):
self.num = 1
En esta pregunta descubrí que en ambos casos,
bar = Foo()
bar.num += 1
es una operación bien definida.
Entiendo que el primer método me dará una variable de clase, mientras que el segundo no. Sin embargo, si no necesito una variable de clase, pero solo necesito establecer un valor predeterminado para mis variables de instancia, ¿son ambos métodos igualmente buenos? ¿O uno de ellos más 'pitónico' que el otro?
Una cosa que he notado es que en el tutorial de Django, usan el segundo método para declarar modelos. Personalmente, creo que el segundo método es más elegante, pero me gustaría saber cuál es la forma "estándar".
self.attr = attr or []
dentro del__init__
método. Logra el mismo resultado (creo) y sigue siendo claro y legible.Los dos fragmentos hacen cosas diferentes, por lo que no es una cuestión de gustos, sino de cuál es el comportamiento correcto en su contexto. La documentación de Python explica la diferencia, pero aquí hay algunos ejemplos:
Anexo A
class Foo: def __init__(self): self.num = 1
Esto se une
num
a las instancias de Foo . El cambio a este campo no se propaga a otras instancias.Así:
>>> foo1 = Foo() >>> foo2 = Foo() >>> foo1.num = 2 >>> foo2.num 1
Anexo B
class Bar: num = 1
Esto se une
num
a la clase Bar . ¡Los cambios se propagan!>>> bar1 = Bar() >>> bar2 = Bar() >>> bar1.num = 2 #this creates an INSTANCE variable that HIDES the propagation >>> bar2.num 1 >>> Bar.num = 3 >>> bar2.num 3 >>> bar1.num 2 >>> bar1.__class__.num 3
Respuesta real
El código en la exhibición B es completamente incorrecto para esto: ¿por qué querría vincular un atributo de clase (valor predeterminado en la creación de la instancia) a la instancia única?
El código de la exhibición A está bien.
Si desea dar valores predeterminados, por ejemplo, variables en su constructor, sin embargo, haría esto:
class Foo: def __init__(self, num = None): self.num = num if num is not None else 1
...o incluso:
class Foo: DEFAULT_NUM = 1 def __init__(self, num = None): self.num = num if num is not None else DEFAULT_NUM
... o incluso: (preferible, ¡pero si y solo si se trata de tipos inmutables!)
class Foo: def __init__(self, num = 1): self.num = num
De esta forma puedes hacer:
foo1 = Foo(4) foo2 = Foo() #use default
fuente
def __init__(self, num = None)
El uso de miembros de clase para dar valores predeterminados funciona muy bien siempre que tenga cuidado de hacerlo solo con valores inmutables. Si intenta hacerlo con una lista o un dict, sería bastante mortal. También funciona cuando el atributo de instancia es una referencia a una clase siempre que el valor predeterminado sea Ninguno.
He visto esta técnica utilizada con mucho éxito en reposicionamiento, que es un marco que se ejecuta sobre Zope. La ventaja aquí no es solo que cuando su clase persiste en la base de datos, solo deben guardarse los atributos no predeterminados, sino también cuando necesita agregar un nuevo campo al esquema, todos los objetos existentes ven el nuevo campo con su valor predeterminado. valor sin necesidad de cambiar realmente los datos almacenados.
También encuentro que funciona bien en la codificación más general, pero es una cuestión de estilo. Usa lo que sea con lo que estés más feliz.
fuente
Usar miembros de clase para valores predeterminados de variables de instancia no es una buena idea, y es la primera vez que veo que se menciona esta idea. Funciona en su ejemplo, pero puede fallar en muchos casos. Por ejemplo, si el valor es mutable, mutarlo en una instancia sin modificar alterará el valor predeterminado:
>>> class c: ... l = [] ... >>> x = c() >>> y = c() >>> x.l [] >>> y.l [] >>> x.l.append(10) >>> y.l [10] >>> c.l [10]
fuente
[]
se clonó allí;x.l
yy.l
son tanto nombres malvados como valores inmediatos diferentes vinculados al mismo referente. (Términos de abogado de idiomas inventados)También puede declarar variables de clase como Ninguna, lo que evitará la propagación. Esto es útil cuando necesita una clase bien definida y desea evitar AttributeErrors. Por ejemplo:
>>> class TestClass(object): ... t = None ... >>> test = TestClass() >>> test.t >>> test2 = TestClass() >>> test.t = 'test' >>> test.t 'test' >>> test2.t >>>
Además, si necesita valores predeterminados:
>>> class TestClassDefaults(object): ... t = None ... def __init__(self, t=None): ... self.t = t ... >>> test = TestClassDefaults() >>> test.t >>> test2 = TestClassDefaults([]) >>> test2.t [] >>> test.t >>>
Por supuesto, siga la información en las otras respuestas sobre el uso de tipos mutables frente a inmutables como predeterminado en
__init__
.fuente
Con las clases de datos , una característica agregada en Python 3.7, ahora hay otra forma (bastante conveniente) de lograr establecer valores predeterminados en instancias de clases. El decorador
dataclass
generará automáticamente algunos métodos en su clase, como el constructor. Como señala la documentación vinculada anteriormente, "[l] as variables miembro a utilizar en estos métodos generados se definen mediante anotaciones de tipo PEP 526 ".Considerando el ejemplo de OP, podríamos implementarlo así:
from dataclasses import dataclass @dataclass class Foo: num: int = 0
Al construir un objeto del tipo de esta clase, opcionalmente podríamos sobrescribir el valor.
print('Default val: {}'.format(Foo())) # Default val: Foo(num=0) print('Custom val: {}'.format(Foo(num=5))) # Custom val: Foo(num=5)
fuente