Python Language Métodos enlazados, no enlazados y estáticos.


Ejemplo

La idea de métodos enlazados y no enlazados se eliminó en Python 3 . En Python 3 cuando declara un método dentro de una clase, está usando una palabra clave def , creando así un objeto de función. Esta es una función regular, y la clase circundante funciona como su espacio de nombres. En el siguiente ejemplo, declaramos el método f dentro de la clase A , y se convierte en una función Af :

Python 3.x 3.0
class A(object):
    def f(self, x):
        return 2 * x
A.f
# <function A.f at ...>  (in Python 3.x)

En Python 2, el comportamiento fue diferente: los objetos de función dentro de la clase fueron reemplazados implícitamente por objetos de tipo instancemethod , que se denominaron métodos no vinculados porque no estaban vinculados a ninguna instancia de clase en particular. Fue posible acceder a la función subyacente utilizando la propiedad .__func__ .

Python 2.x 2.3
A.f
# <unbound method A.f>   (in Python 2.x)
A.f.__class__
# <type 'instancemethod'>
A.f.__func__
# <function f at ...>

Los últimos comportamientos se confirman mediante inspección: los métodos se reconocen como funciones en Python 3, mientras que la distinción se mantiene en Python 2.

Python 3.x 3.0
import inspect

inspect.isfunction(A.f)
# True
inspect.ismethod(A.f)
# False
Python 2.x 2.3
import inspect

inspect.isfunction(A.f)
# False
inspect.ismethod(A.f)
# True

En ambas versiones de Python function / method Af se puede llamar directamente, siempre que pase una instancia de clase A como primer argumento.

A.f(1, 7)
# Python 2: TypeError: unbound method f() must be called with
#                      A instance as first argument (got int instance instead) 
# Python 3: 14   
a = A()
A.f(a, 20)
# Python 2 & 3: 40

Supongamos ahora que a es una instancia de la clase A , lo que es af entonces? Bueno, intuitivamente este debe ser el mismo método f de clase A , sólo que debe de alguna manera "saber" que se aplica al objeto a - método en Python esto se llama unida a a .

Los detalles esenciales son los siguientes: writing af invoca el método magic __getattribute__ de a , que primero verifica si a tiene un atributo llamado f (no lo hace), luego verifica la clase A si contiene un método con ese nombre (lo hace), y crea un nuevo objeto m del method de tipo que tiene la referencia al Af original en m.__func__ , y una referencia al objeto a en m.__self__ . Cuando este objeto se llama como una función, simplemente hace lo siguiente: m(...) => m.__func__(m.__self__, ...) . Por lo tanto, este objeto se denomina método enlazado porque, cuando se invoca, sabe que debe proporcionar el objeto al que estaba vinculado como primer argumento. (Estas cosas funcionan de la misma manera en Python 2 y 3).

a = A()
a.f
# <bound method A.f of <__main__.A object at ...>>
a.f(2)
# 4

# Note: the bound method object a.f is recreated *every time* you call it:
a.f is a.f  # False
# As a performance optimization you can store the bound method in the object's
# __dict__, in which case the method object will remain fixed:
a.f = a.f
a.f is a.f  # True

Finalmente, Python tiene métodos de clase y métodos estáticos , tipos especiales de métodos. Los métodos de clase funcionan de la misma manera que los métodos regulares, excepto que cuando se invocan en un objeto, se unen a la clase del objeto en lugar de al objeto. Así m.__self__ = type(a) . Cuando llama a dicho método enlazado, pasa la clase de a como primer argumento. Los métodos estáticos son incluso más simples: no vinculan nada en absoluto, y simplemente devuelven la función subyacente sin ninguna transformación.

class D(object):
    multiplier = 2

    @classmethod
    def f(cls, x):
        return cls.multiplier * x

    @staticmethod
    def g(name):
        print("Hello, %s" % name)

D.f
# <bound method type.f of <class '__main__.D'>>
D.f(12)
# 24
D.g
# <function D.g at ...>
D.g("world")
# Hello, world

Tenga en cuenta que los métodos de clase están vinculados a la clase incluso cuando se accede a ellos en la instancia:

d = D()
d.multiplier = 1337
(D.multiplier, d.multiplier)
# (2, 1337)
d.f
# <bound method D.f of <class '__main__.D'>>
d.f(10)
# 20

Vale la pena señalar que en el nivel más bajo, las funciones, los métodos, los métodos estáticos, etc., son en realidad descriptores que invocan los métodos especiales __get__ , __set __ y opcionalmente __del__ . Para más detalles sobre métodos de clase y métodos estáticos: