¿Cómo debo declarar valores predeterminados para variables de instancia en Python?

¿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 lo hará. 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 ‘pythonico’ 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 el método “estándar”.

Extendiendo la respuesta de bp, quería mostrarte lo que quería decir con tipos inmutables.

Primero, esto está bien:

 >>> class TestB(): ... def __init__(self, attr=1): ... self.attr = attr ... >>> a = TestB() >>> b = TestB() >>> a.attr = 2 >>> a.attr 2 >>> b.attr 1 

Sin embargo, esto solo funciona para tipos inmutables (inmutables). Si el valor predeterminado era mutable (lo que significa que se puede reemplazar), esto sucedería en su lugar:

 >>> class Test(): ... def __init__(self, attr=[]): ... self.attr = attr ... >>> a = Test() >>> b = Test() >>> a.attr.append(1) >>> a.attr [1] >>> b.attr [1] >>> 

Tenga en cuenta que tanto a como b tienen un atributo compartido. Esto es a menudo no deseado.

Esta es la forma Pythonic de definir valores predeterminados para variables de instancia, cuando el tipo es mutable:

 >>> class TestC(): ... def __init__(self, attr=None): ... if attr is None: ... attr = [] ... self.attr = attr ... >>> a = TestC() >>> b = TestC() >>> a.attr.append(1) >>> a.attr [1] >>> b.attr [] 

La razón por la que funciona mi primer fragmento de código es que, con tipos inmutables, Python crea una nueva instancia de él siempre que lo desee. Si necesitabas agregar 1 a 1, Python crea un nuevo 2 para ti, porque el antiguo 1 no se puede cambiar. La razón es sobre todo para el hash, creo.

Los dos fragmentos hacen cosas diferentes, por lo que no es una cuestión de gusto sino de cuál es el comportamiento correcto en su contexto. La documentación de Python explica la diferencia, pero aquí hay algunos ejemplos:

Exhibición A

 class Foo: def __init__(self): self.num = 1 

Esto 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 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

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 ‘pythonico’ que el otro?

El código en la muestra B es simplemente erróneo para esto: ¿por qué querría unir un atributo de clase (valor predeterminado en la creación de la instancia) a la única instancia?

El código en la exposición A está bien.

Sin embargo, si quiere dar valores predeterminados para las variables de instancia en su constructor, haría esto:

 class Foo: def __init__(num = None): self.num = num if num is not None else 1 

…o incluso:

 class Foo: DEFAULT_NUM = 1 def __init__(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__(num = 1): self.num = num 

De esta manera puedes hacer:

 foo1 = Foo(4) foo2 = Foo() #use default 

El uso de miembros de la clase para dar valores predeterminados funciona muy bien siempre y cuando tenga cuidado de hacerlo solo con valores inmutables. Si intentas hacerlo con una lista o un dictado, eso sería bastante mortal. También funciona cuando el atributo de instancia es una referencia a una clase, siempre y cuando el valor predeterminado sea Ninguno.

He visto esta técnica utilizada con éxito en la reposición, 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 que también cuando necesita agregar un nuevo campo en el esquema, todos los objetos existentes ven el nuevo campo con su valor predeterminado Valor sin necesidad de cambiar realmente los datos almacenados.

Me parece que también funciona bien en la encoding más general, pero es una cuestión de estilo. Usa lo que sea más feliz con.

Usar miembros de la clase para los valores predeterminados de las variables de instancia no es una buena idea, y es la primera vez que veo esta idea mencionada. Funciona en su ejemplo, pero puede fallar en muchos casos. Por ejemplo, si el valor es mutable, mutarlo en una instancia no modificada alterará el valor predeterminado:

 >>> class c: ... l = [] ... >>> x = c() >>> y = c() >>> xl [] >>> yl [] >>> xlappend(10) >>> yl [10] >>> cl [10] 

También puede declarar las 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 >>> 

También 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, aún siga la información en las otras respuestas sobre el uso de tipos mutables e inmutables como predeterminados en __init__ .