otra - poo python 3
Miembros de la clase Python (6)
En python, es posible tener variables de clase y variables de instancia del mismo nombre. Se ubican por separado en la memoria y se accede de forma bastante diferente.
En tu código:
class Node(object):
element, left, right = None
def __init__(self, element):
self.element = element
self.left = self.right = None
El primer conjunto de variables (fuera de la función __init__
) se llaman variables de clase. A continuación, se puede acceder a ellos utilizando Node.element
, etc. Estos son equivalentes a las variables de miembros estáticos en C ++, y todas las instancias de la clase los comparten.
El segundo conjunto de variables (dentro de la función __init__
) se denominan variables de instancia. A estos se accede a través del objeto self
, por ejemplo, self.element
, o por el nombre de instancia, por ejemplo, myNode.element
fuera de la clase.
Es importante tener en cuenta que debe usar la forma self.variable
o Node.variable
para acceder a cualquiera de estos dentro de una función miembro. El solo hecho de acceder a la variable
intentará acceder a una variable local llamada variable
.
Acabo de aprender Python y vengo de C, así que avíseme si tengo confusión / confusión entre ambos.
Supongamos que tengo la siguiente clase:
class Node(object):
def __init__(self, element):
self.element = element
self.left = self.right = None
@classmethod
def tree(cls, element, left, right):
node = cls(element)
node.left = left
node.right = right
return node
Esta es una clase llamada Node
, que sobrecarga el constructor, para poder manejar diferentes argumentos si es necesario.
¿Cuál es la diferencia entre definir self.element
en __init__
solamente (como se muestra arriba) en lugar de hacer lo siguiente?
class Node(object):
element, left, right = None
def __init__(self, element):
self.element = element
self.left = self.right = None
¿El elemento propio no self.element
en __init__
igual que la variable de element
la clase definida? ¿No sobrescribiría ese element
de None
al valor del element
pasado a __init__
?
La parte importante es el self
argumento para __init__
. De hecho, en cualquier método de instancia, este será el primer argumento. Esto se hace por diseño; en Python, el único momento en que realmente tiene acceso a la instancia es durante las llamadas al método, y se muestra explícitamente con el self
argumento.
Cuando estás dentro de una definición de class
, todavía no tienes instancias, entonces lo que realmente estás modificando es la clase misma. Por lo tanto, si defines atributos en el nivel de clase, entonces realmente se convierten en atributos de clase, y no instancia.
Si lo comparas con un C (++), probablemente puedas decir que las "clases" en esos idiomas son básicamente planos para los objetos que representan. "Estos objetos tendrán atributos foo
y bar
, y, además, los siguientes métodos". En Python, sin embargo, las clases son objetos en sí mismos, y su principal fortaleza es que pueden crear copias (instancias) de ellos mismos, que también usan los métodos de la clase. Por lo tanto, es más como "Tendrás foo
y bar
como atributos de clase y, además, el siguiente método que deberás usar para crear instancias".
Entonces, en lugar de un plan, es más un paso a paso.
Uno es un atributo de clase, mientras que el otro es un atributo de instancia. Son diferentes, pero están estrechamente relacionados entre sí de manera que a veces se ven iguales.
Tiene que ver con la forma en que python busca los atributos. Hay una jerarquía. En casos simples, podría verse así:
instance -> Subclass -> Superclass -> object (built-in type)
Cuando busca un atributo en una instance
como esta ...
`instance.val`
... lo que realmente sucede es que primero , Python busca val
en la instancia misma. Entonces, si no encuentra val
, se ve en su clase, Subclass
. Entonces, si no encuentra val
allí, se ve en el padre de Subclass
, Superclass
. Esto significa que cuando haces esto ...
>>> class Foo():
foovar = 10
def __init__(self, val):
self.selfvar = val
... todas las instancias de Foo
comparten foovar
, pero tienen sus propios selfvar
distintivos. Aquí hay un ejemplo simple y concreto de cómo funciona eso:
>>> f = Foo(5)
>>> f.foovar
10
>>> Foo.foovar
10
Si no tocamos foovar
, es igual para f
y para Foo
. Pero si cambiamos f.foovar
...
>>> f.foovar = 5
>>> f.foovar
5
>>> Foo.foovar
10
... agregamos un atributo de instancia que enmascara efectivamente el valor de Foo.foovar
. Ahora si cambiamos Foo.foovar
directamente, no afecta nuestra instancia foo
:
>>> Foo.foovar = 7
>>> f.foovar
5
Pero sí afecta una nueva instancia de foo
:
>>> Foo(5).foovar
7
También tenga en cuenta que los objetos mutables añaden otra capa de indirección (como mgilson me recordó). Aquí, f.foovar
refiere al mismo objeto que Foo.foovar
, por lo que cuando modifica el objeto, los cambios se propagan por la jerarquía:
>>> Foo.foovar = [1]
>>> f = Foo(5)
>>> f.foovar[0] = 99
>>> Foo.foovar
[99]
cuando intenta acceder a la variable con una clase, solo busca en ella
cls.__dict__
pero cuando intenta acceder a la variable con instancia se ve primero
self.__dict__
si lo encuentra, luego regresa o si no puede encontrarlo, entonces también busca
cls.__dict__
aquí cls es la clase
class Test:
temp_1=10
temp_2=20
def __init__(self):
self.test_1=10
self.test_2=20
@classmethod
def c_test(cls):
pass
def t_method(self):
pass
print Test.__dict__
print Test().__dict__
Salida :
{''c_test'': <classmethod object at 0x7fede8f35a60>, ''__module__'': ''__main__'', ''t_method'': <function t_method at 0x7fede8f336e0>, ''temp_1'': 10, ''__doc__'': None, ''__init__'': <function __init__ at 0x7fede8f335f0>, ''temp_2'': 20}
{''test_2'': 20, ''test_1'': 10}
Para atributo especial clase de detalle
self.element dentro del constructor es una variable de instancia (si un objeto nodo modifica su valor solo cambia para este objeto) donde el de la segunda versión es una variable de clase (por lo que si un objeto nodo modifica su valor, cambiará para todos objetos nodo).
La analogía en C ++ sería no estática frente a variables de miembros estáticos en su clase.
self.element en el init es una variable de instancia, puede obtener / configurarlo en cualquier otra función de miembro escribiendo self.element. El elemento declarado en la clase es la variable de clase, puede obtenerlo / configurar escribiendo Node.element.