python oop function methods

subplot python



¿Cuál es la diferencia entre una función, un método independiente y un método vinculado? (5)

es bastante difícil de entender

Bueno, es un tema bastante difícil, y tiene que ver con los descriptores.

Comencemos con la función. Todo está claro aquí; simplemente lo llama, todos los argumentos suministrados se pasan mientras se ejecuta:

>>> f = A.__dict__[''f1''] >>> f(1) 1

Regular TypeError se plantea en caso de cualquier problema con el número de parámetros:

>>> f() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: f1() takes exactly 1 argument (0 given)

Ahora, métodos. Los métodos son funciones con un poco de especias. Los descriptores vienen en juego aquí. Como se describe en el Modelo de datos , A.f1 y A().f1 se traducen en A.__dict__[''f1''].__get__(None, A) y type(a).__dict__[''f1''].__get__(a, type(a)) respectivamente. Y los resultados de estos __get__ ''difieren de la función raw f1 . Estos objetos son envoltorios alrededor del f1 original y contienen alguna lógica adicional.

En el caso del unbound method independiente, esta lógica incluye una comprobación de si el primer argumento es una instancia de A :

>>> f = A.f1 >>> f() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unbound method f1() must be called with A instance as first argument (got nothing instead) >>> f(1) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unbound method f1() must be called with A instance as first argument (got int instance instead)

Si esta comprobación tiene éxito, ejecuta el f1 original con esa instancia como primer argumento:

>>> f(A()) <__main__.A object at 0x800f238d0>

Tenga en cuenta que im_self atributo im_self es None :

>>> f.im_self is None True

En el caso de un bound method esta lógica proporciona inmediatamente a f1 original una instancia de A que se creó (esta instancia se almacena en el atributo im_self ):

>>> f = A().f1 >>> f.im_self <__main__.A object at 0x800f23950> >>> f() <__main__.A object at 0x800f23950>

Entonces, bound significa que la función subyacente está vinculada a alguna instancia. unbound significa que todavía está vinculado, pero solo a una clase.

Estoy haciendo esta pregunta debido a una discusión sobre el hilo de comentario de esta respuesta . Estoy al 90% del camino para entenderlo.

In [1]: class A(object): # class named ''A'' ...: def f1(self): pass ...: In [2]: a = A() # an instance

f1 existe en tres formas diferentes:

In [3]: a.f1 # a bound method Out[3]: <bound method a.f1 of <__main__.A object at 0x039BE870>> In [4]: A.f1 # an unbound method Out[4]: <unbound method A.f1> In [5]: a.__dict__[''f1''] # doesn''t exist KeyError: ''f1'' In [6]: A.__dict__[''f1''] # a function Out[6]: <function __main__.f1>

¿Cuál es la diferencia entre el método vinculado , el método de unión libre y los objetos de función , todos los cuales están descritos por f1? ¿Cómo se llaman estos tres objetos? ¿Cómo pueden transformarse el uno en el otro? La documentation sobre esto es bastante difícil de entender.


Consulte la documentación de Python 2 y Python 3 para obtener más detalles.

Mi interpretación es la siguiente.

Fragmentos de Function clase:

Python 3:

class Function(object): . . . def __get__(self, obj, objtype=None): "Simulate func_descr_get() in Objects/funcobject.c" if obj is None: return self return types.MethodType(self, obj)

Python 2:

class Function(object): . . . def __get__(self, obj, objtype=None): "Simulate func_descr_get() in Objects/funcobject.c" return types.MethodType(self, obj, objtype)

  1. Si se llama a una función sin clase o instancia, es una función simple.
  2. Si se llama a una función desde una clase o una instancia, se llama a su __get__ para recuperar la función envuelta:
    a. Bx es lo mismo que B.__dict__[''x''].__get__(None, B) . En Python 3, esto devuelve una función simple. En Python 2, esto devuelve una función independiente.

    segundo. bx es igual que el type(b).__dict__[''x''].__get__(b, type(b) . Esto devolverá un método enlazado tanto en Python 2 como en Python 3, lo que significa que self pasará implícitamente como primer argumento.


Objeto de función es un objeto invocable creado por una definición de función. Tanto los métodos vinculados como los no vinculados son objetos invocables creados por un Descriptor llamado por el operador binario de puntos.

Los objetos de método enlazados y no im_func tienen 3 propiedades principales: im_func es el objeto de función definido en la clase, im_class es la clase e im_self es la instancia de clase. Para los métodos im_self , im_self es None .

Cuando se llama a un método enlazado, llama a im_func con im_self ya que el primer parámetro siguió sus parámetros de llamada. los métodos unbound llaman a la función subyacente solo con sus parámetros de llamada.


Una función es creada por la declaración def , o por lambda . Bajo Python 2, cuando una función aparece dentro del cuerpo de una declaración de class (o se pasa a una llamada de construcción de clase de type ), se transforma en un método independiente . (Python 3 no tiene métodos independientes, ver más abajo.) Cuando se accede a una función en una instancia de clase, se transforma en un método enlazado , que automáticamente suministra la instancia al método como el primer parámetro self .

def f1(self): pass

Aquí f1 es una función .

class C(object): f1 = f1

Ahora C.f1 es un método C.f1 .

>>> C.f1 <unbound method C.f1> >>> C.f1.im_func is f1 True

También podemos usar el constructor de clase de type :

>>> C2 = type(''C2'', (object,), {''f1'': f1}) >>> C2.f1 <unbound method C2.f1>

Podemos convertir f1 a un método independiente de forma manual:

>>> import types >>> types.MethodType(f1, None, C) <unbound method C.f1>

Los métodos no vinculados están vinculados por el acceso en una instancia de clase:

>>> C().f1 <bound method C.f1 of <__main__.C object at 0x2abeecf87250>>

El acceso se traduce en llamadas a través del protocolo de descripción:

>>> C.f1.__get__(C(), C) <bound method C.f1 of <__main__.C object at 0x2abeecf871d0>>

Combinando estos:

>>> types.MethodType(f1, None, C).__get__(C(), C) <bound method C.f1 of <__main__.C object at 0x2abeecf87310>>

O directamente:

>>> types.MethodType(f1, C(), C) <bound method C.f1 of <__main__.C object at 0x2abeecf871d0>>

La principal diferencia entre una función y un método independiente es que este último sabe a qué clase está destinada; llamar o vincular un método independiente requiere una instancia de su tipo de clase:

>>> f1(None) >>> C.f1(None) TypeError: unbound method f1() must be called with C instance as first argument (got NoneType instance instead) >>> class D(object): pass >>> f1.__get__(D(), D) <bound method D.f1 of <__main__.D object at 0x7f6c98cfe290>> >>> C.f1.__get__(D(), D) <unbound method C.f1>

Dado que la diferencia entre una función y un método independiente es bastante mínima, Python 3 se deshace de la distinción; en Python 3 el acceso a una función en una instancia de clase solo le da la función en sí:

>>> C.f1 <function f1 at 0x7fdd06c4cd40> >>> C.f1 is f1 True

En Python 2 y Python 3, entonces, estos tres son equivalentes:

f1(C()) C.f1(C()) C().f1()

La vinculación de una función a una instancia tiene el efecto de fijar su primer parámetro (convencionalmente llamado self ) a la instancia. Por lo tanto, el método vinculado C().f1 es equivalente a cualquiera de:

(lamdba *args, **kwargs: f1(C(), *args, **kwargs)) functools.partial(f1, C())


Una cosa interesante que vi hoy es que, cuando asigno una función a un miembro de la clase, se convierte en un método independiente. Como:

class Test(object): @classmethod def initialize_class(cls): def print_string(self, str): print(str) # Here if I do print(print_string), I see a function cls.print_proc = print_string # Here if I do print(cls.print_proc), I see an unbound method; so if I # get a Test object o, I can call o.print_proc("Hello")