¿Hay alguna distinción significativa entre:
class A(object):
foo = 5 # some default value
vs.
class B(object):
def __init__(self, foo=5):
self.foo = foo
Si está creando muchas instancias, ¿hay alguna diferencia en el rendimiento o los requisitos de espacio para los dos estilos? Cuando lee el código, ¿considera que el significado de los dos estilos es significativamente diferente?
python
attributes
member-variables
Dan Homerick
fuente
fuente
Respuestas:
Más allá de las consideraciones de rendimiento, hay una diferencia semántica significativa . En el caso del atributo de clase, solo se hace referencia a un objeto. En el conjunto de atributos de instancia en la instanciación, puede haber varios objetos a los que se hace referencia. Por ejemplo
fuente
int
ystr
todavía se adjuntan con cada instancia en lugar de clase.int
ystr
son también compartidos, exactamente de la misma manera. Puede verificar eso fácilmente conis
oid
. O simplemente mire en cada instancia__dict__
y en la clase__dict__
. Por lo general, no importa mucho si los tipos inmutables se comparten o no.a.foo = 5
, en ambos casos veráb.foo
regresar[]
. Esto se debe a que, en el primer caso, está sobrescribiendo el atributo de clasea.foo
con un nuevo atributo de instancia del mismo nombre.La diferencia es que el atributo en la clase es compartido por todas las instancias. El atributo en una instancia es exclusivo de esa instancia.
Si viene de C ++, los atributos en la clase son más como variables miembro estáticas.
fuente
>>> class A(object): foo = 5
>>> a, b = A(), A()
>>> a.foo = 10
>>> b.foo
5
a.foo.append(5)
está mutando el valor al que sea.foo
refiere, mientras sea.foo = 5
está convirtiendoa.foo
en un nuevo nombre para el valor5
. Entonces, terminas con un atributo de instancia que oculta el atributo de clase. Pruebe lo mismoa.foo = 5
en la versión de Alex y verá que nob.foo
ha cambiado.Aquí hay una muy buena publicación , y resumirla como a continuación
Y en forma visual
Asignación de atributo de clase
Si un atributo de clase se establece accediendo a la clase, anulará el valor para todas las instancias
Si se establece una variable de clase accediendo a una instancia, anulará el valor solo para esa instancia . Esto esencialmente anula la variable de clase y la convierte en una variable de instancia disponible, intuitivamente, solo para esa instancia .
¿Cuándo usarías el atributo de clase?
Almacenamiento de constantes . Como se puede acceder a los atributos de clase como atributos de la clase en sí, a menudo es bueno usarlos para almacenar constantes específicas de clase y de toda la clase
Definición de valores por defecto . Como ejemplo trivial, podríamos crear una lista acotada (es decir, una lista que solo puede contener un cierto número de elementos o menos) y elegir tener un límite predeterminado de 10 elementos.
fuente
Dado que las personas en los comentarios aquí y en otras dos preguntas marcadas como dups parecen estar confundidas acerca de esto de la misma manera, creo que vale la pena agregar una respuesta adicional además de la de Alex Coventry .
El hecho de que Alex asigne un valor de tipo mutable, como una lista, no tiene nada que ver con si las cosas se comparten o no. Podemos ver esto con la
id
función o elis
operador:(Si se pregunta por qué lo usé en
object()
lugar de, por ejemplo,5
eso es para evitar toparme con otros dos problemas que no quiero abordar aquí; por dos razones diferentes, los correos electrónicos creados por separado5
pueden terminar siendo misma instancia del número,5
peroobject()
no se pueden crear s completamente por separado ).Entonces, ¿por qué
a.foo.append(5)
en el ejemplo de Alex afectab.foo
, peroa.foo = 5
en mi ejemplo no afecta? Bueno, tratea.foo = 5
en el ejemplo de Alex, y el aviso de que no afectab.foo
existe tampoco .a.foo = 5
solo se está convirtiendoa.foo
en un nombre para5
. Eso no afectab.foo
, ni ningún otro nombre para el valor anterior al quea.foo
solía referirse. * Es un poco complicado que estemos creando un atributo de instancia que oculte un atributo de clase, ** pero una vez que lo obtienes, nada complicado es sucediendo aquíCon suerte, ahora es obvio por qué Alex usó una lista: el hecho de que pueda mutar una lista significa que es más fácil mostrar que dos variables nombran la misma lista, y también significa que es más importante en el código de la vida real saber si tiene dos listas o no. Dos nombres para la misma lista.
* La confusión para las personas que provienen de un lenguaje como C ++ es que en Python, los valores no se almacenan en variables. Los valores viven en tierra de valor, por sí solos, las variables son solo nombres para valores, y la asignación solo crea un nuevo nombre para un valor. Si ayuda, piense en cada variable de Python como en
shared_ptr<T>
lugar de aT
.** Algunas personas aprovechan esto usando un atributo de clase como un "valor predeterminado" para un atributo de instancia que las instancias pueden o no establecer. Esto puede ser útil en algunos casos, pero también puede ser confuso, así que tenga cuidado con eso.
fuente
Hay una situación más.
Los atributos de clase e instancia son Descriptor .
Arriba saldrá:
¡El mismo tipo de acceso a la instancia a través de la clase o instancia devuelve un resultado diferente!
Y encontré en c.PyObject_GenericGetAttr definición , y una gran publicación .
Explique
fuente