clases - python poo tutorial
Python: diferencia entre clase y atributos de instancia (6)
Aquí hay una post muy buena y resúmala como se muestra a continuación.
class Bar(object):
## No need for dot syntax
class_var = 1
def __init__(self, i_var):
self.i_var = i_var
## Need dot syntax as we''ve left scope of class namespace
Bar.class_var
## 1
foo = MyClass(2)
## Finds i_var in foo''s instance namespace
foo.i_var
## 2
## Doesn''t find class_var in instance namespace…
## So look''s in class namespace (Bar.__dict__)
foo.class_var
## 1
Y en forma visual
Asignación de atributos de clase
Si se establece un atributo de clase accediendo a la clase, se anulará el valor para todas las instancias
foo = Bar(2) foo.class_var ## 1 Bar.class_var = 2 foo.class_var ## 2
Si se establece una variable de clase accediendo a una instancia, anulará el valor solo para esa instancia . Esto básicamente anula la variable de clase y la convierte en una variable de instancia disponible, intuitivamente, solo para esa instancia .
foo = Bar(2) foo.class_var ## 1 foo.class_var = 2 foo.class_var ## 2 Bar.class_var ## 1
¿Cuándo usarías el atributo de clase?
Almacenamiento de constantes . Como se puede acceder a los atributos de la clase como atributos de la misma clase, a menudo es bueno usarlos para almacenar constantes de Class-wide, Class-specific
class Circle(object): pi = 3.14159 def __init__(self, radius): self.radius = radius def area(self): return Circle.pi * self.radius * self.radius Circle.pi ## 3.14159 c = Circle(10) c.pi ## 3.14159 c.area() ## 314.159
Definiendo valores predeterminados . Como un ejemplo trivial, podemos crear una lista limitada (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
class MyClass(object): limit = 10 def __init__(self): self.data = [] def item(self, i): return self.data[i] def add(self, e): if len(self.data) >= self.limit: raise Exception("Too many elements") self.data.append(e) MyClass.limit ## 10
¿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 los requisitos de rendimiento o espacio para los dos estilos? Cuando lee el código, ¿considera que el significado de los dos estilos es significativamente diferente?
Como las personas en los comentarios aquí y en otras dos preguntas marcadas como dúplex parecen estar confundidas al respecto 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 esté asignando 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 función id
o el operador is
:
>>> class A: foo = object()
>>> a, b = A(), A()
>>> a.foo is b.foo
True
>>> class A:
... def __init__(self): self.foo = object()
>>> a, b = A(), A()
>>> a.foo is b.foo
False
(Si te estás preguntando por qué utilicé object()
lugar de, digamos, 5
, eso es para evitar toparme con otros dos problemas en los que no quiero entrar, por dos motivos diferentes, creado por separado 5
s puede terminar siendo la misma instancia del número 5
Pero el object()
s completamente creado por separado no puede).
Entonces, ¿por qué es que a.foo.append(5)
en el ejemplo de Alex afecta a b.foo
, pero a.foo = 5
en mi ejemplo no? Bueno, pruebe a.foo = 5
en el ejemplo de Alex, y observe que tampoco afecta a b.foo
allí.
a.foo = 5
simplemente está haciendo a.foo
en un nombre para 5
. Eso no afecta a b.foo
, ni a ningún otro nombre para el valor anterior al que a.foo
solía referirse. * Es un poco complicado crear un atributo de instancia que oculte un atributo de clase, ** pero una vez que entiéndelo, nada complicado está sucediendo aquí.
Esperemos que ahora sea 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 usted tiene dos listas o 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 las variables. Los valores viven en valor-tierra, 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 shared_ptr<T>
lugar de T
** Algunas personas aprovechan esto al usar 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 ten cuidado.
Hay una situación más.
Los atributos de clase e instancia son Descriptor .
# -*- encoding: utf-8 -*-
class RevealAccess(object):
def __init__(self, initval=None, name=''var''):
self.val = initval
self.name = name
def __get__(self, obj, objtype):
return self.val
class Base(object):
attr_1 = RevealAccess(10, ''var "x"'')
def __init__(self):
self.attr_2 = RevealAccess(10, ''var "x"'')
def main():
b = Base()
print("Access to class attribute, return: ", Base.attr_1)
print("Access to instance attribute, return: ", b.attr_2)
if __name__ == ''__main__'':
main()
Arriba se producirá:
(''Access to class attribute, return: '', 10)
(''Access to instance attribute, return: '', <__main__.RevealAccess object at 0x10184eb50>)
¡El mismo tipo de acceso a instancia a través de clase o instancia devuelve un resultado diferente!
Y lo encontré en la definición de c.PyObject_GenericGetAttr , y una gran post .
Explique
Si el atributo se encuentra en el diccionario de las clases que lo componen. los objetos MRO, luego verifique si el atributo que se busca apunta a un Descriptor de datos (que no es más que una clase que implementa los
__get__
y__set__
). Si lo hace, resuelva la búsqueda de atributos llamando al método__get__
del Descriptor de datos (líneas 28-33).
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 proviene de C ++, los atributos de la clase son más parecidos a las variables estáticas de los miembros.
Más allá de las consideraciones de rendimiento, existe una diferencia semántica significativa. En el caso de atributo de clase, solo se hace referencia a un objeto. En instancia-atributo-conjunto-en-instanciación, puede haber múltiples objetos a los que se hace referencia. Por ejemplo
>>> class A: foo = []
>>> a, b = A(), A()
>>> a.foo.append(5)
>>> b.foo
[5]
>>> class A:
... def __init__(self): self.foo = []
>>> a, b = A(), A()
>>> a.foo.append(5)
>>> b.foo
[]
Solo una explicación sobre lo que dijo Alex Coventry, otro Alex (Martelli) abordó una pregunta similar en el grupo de noticias comp.lang.python
años atrás. Examina la diferencia semántica de lo que una persona pretendía frente a lo que obtuvo (mediante el uso de variables de instancia).
http://groups.google.com/group/comp.lang.python/msg/5914d297aff35fae?hl=en