Diferencias de métodos de clase en Python: vinculado, no vinculado y estático

Resuelto Franck Mesirard asked hace 15 años • 13 respuestas

¿Cuál es la diferencia entre los siguientes métodos de clase?

¿Será que uno es estático y el otro no?

class Test(object):
  def method_one(self):
    print "Called method_one"

  def method_two():
    print "Called method_two"

a_test = Test()
a_test.method_one()
a_test.method_two()
Franck Mesirard avatar Sep 22 '08 17:09 Franck Mesirard
Aceptado

En Python, existe una distinción entre métodos vinculados y no vinculados .

Básicamente, una llamada a una función miembro (como method_one), una función vinculada

a_test.method_one()

se traduce a

Test.method_one(a_test)

es decir, una llamada a un método independiente. Debido a eso, una llamada a su versión method_twofallará con unTypeError

>>> a_test = Test() 
>>> a_test.method_two()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: method_two() takes no arguments (1 given) 

Puedes cambiar el comportamiento de un método usando un decorador.

class Test(object):
    def method_one(self):
        print "Called method_one"

    @staticmethod
    def method_two():
        print "Called method two"

El decorador le dice a la metaclase predeterminada incorporada type(la clase de una clase, consulte esta pregunta ) que no cree métodos vinculados para method_two.

Ahora, puedes invocar un método estático tanto en una instancia como directamente en la clase:

>>> a_test = Test()
>>> a_test.method_one()
Called method_one
>>> a_test.method_two()
Called method_two
>>> Test.method_two()
Called method_two
Torsten Marek avatar Sep 22 '2008 11:09 Torsten Marek

Los métodos en Python son algo muy, muy simple una vez que entiendes los conceptos básicos del sistema de descriptores. Imagina la siguiente clase:

class C(object):
    def foo(self):
        pass

Ahora echemos un vistazo a esa clase en el shell:

>>> C.foo
<unbound method C.foo>
>>> C.__dict__['foo']
<function foo at 0x17d05b0>

Como puede ver, si accede al fooatributo de la clase, obtendrá un método independiente; sin embargo, dentro del almacenamiento de la clase (el dict) hay una función. ¿Porque eso? La razón de esto es que la clase de su clase implementa un __getattribute__que resuelve descriptores. Suena complejo, pero no lo es. C.fooes aproximadamente equivalente a este código en ese caso especial:

>>> C.__dict__['foo'].__get__(None, C)
<unbound method C.foo>

Esto se debe a que las funciones tienen un __get__método que las convierte en descriptores. Si tienes una instancia de una clase, es casi lo mismo, solo que esa Nonees la instancia de la clase:

>>> c = C()
>>> C.__dict__['foo'].__get__(c, C)
<bound method C.foo of <__main__.C object at 0x17bd4d0>>

Ahora bien, ¿por qué Python hace eso? Porque el objeto del método vincula el primer parámetro de una función a la instancia de la clase. De ahí viene el yo. Ahora bien, a veces no quieres que tu clase convierta una función en un método, ahí es donde staticmethodentra en juego:

 class C(object):
  @staticmethod
  def foo():
   pass

El staticmethoddecorador envuelve su clase e implementa un modelo __get__que devuelve la función envuelta como función y no como método:

>>> C.__dict__['foo'].__get__(None, C)
<function foo at 0x17d0c30>

Espero que eso lo explique.

Armin Ronacher avatar Sep 22 '2008 11:09 Armin Ronacher

Cuando llamas a un miembro de la clase, Python usa automáticamente una referencia al objeto como primer parámetro. La variable selfen realidad no significa nada, es sólo una convención de codificación. Podrías llamarlo gargaloosi quisieras. Dicho esto, la llamada a method_twogeneraría un TypeError, porque Python intenta automáticamente pasar un parámetro (la referencia a su objeto principal) a un método que se definió como sin parámetros.

Para que funcione, puedes agregar esto a tu definición de clase:

method_two = staticmethod(method_two)

o podrías usar el @staticmethod decorador de funciones .

Justin Poliey avatar Sep 22 '2008 10:09 Justin Poliey