Je pose cette question à cause d'une discussion sur le fil de commentaires de cette réponse . Je suis à 90% du chemin pour contourner la tête.
In [1]: class A(object): # class named 'A'
...: def f1(self): pass
...:
In [2]: a = A() # an instance
f1
existe sous trois formes différentes:
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>
Quelle est la différence entre la méthode liée , la méthode non liée et fonction objets, tous décrits par f1? Comment appelle-t-on ces trois objets? Comment peuvent-ils se transformer les uns les autres? Le documentation sur ce genre de choses est assez difficile à comprendre.
Une fonction est créée par l'instruction def
, ou par lambda
. Sous Python 2, lorsqu'une fonction apparaît dans le corps d'une instruction class
(ou est passée à un appel de construction de classe type
), elle est transformée en an méthode non liée. (Python 3 n'a pas de méthodes non liées; voir ci-dessous.) Lorsqu'une fonction est accédée sur une instance de classe, elle est transformée en méthode liée, qui fournit automatiquement l'instance à la méthode en tant que premier paramètre self
.
def f1(self):
pass
Ici f1
Est une fonction.
class C(object):
f1 = f1
Maintenant C.f1
Est une méthode non liée.
>>> C.f1
<unbound method C.f1>
>>> C.f1.im_func is f1
True
Nous pouvons également utiliser le constructeur de classe type
:
>>> C2 = type('C2', (object,), {'f1': f1})
>>> C2.f1
<unbound method C2.f1>
Nous pouvons convertir f1
En une méthode non liée manuellement:
>>> import types
>>> types.MethodType(f1, None, C)
<unbound method C.f1>
Les méthodes non liées sont liées par l'accès à une instance de classe:
>>> C().f1
<bound method C.f1 of <__main__.C object at 0x2abeecf87250>>
L'accès se traduit par un appel via le protocole descripteur:
>>> C.f1.__get__(C(), C)
<bound method C.f1 of <__main__.C object at 0x2abeecf871d0>>
La combinaison de ces:
>>> types.MethodType(f1, None, C).__get__(C(), C)
<bound method C.f1 of <__main__.C object at 0x2abeecf87310>>
Ou directement:
>>> types.MethodType(f1, C(), C)
<bound method C.f1 of <__main__.C object at 0x2abeecf871d0>>
La principale différence entre une fonction et une méthode non liée est que cette dernière sait à quelle classe elle est liée; l'appel ou la liaison d'une méthode non liée nécessite une instance de son type de classe:
>>> 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>
Étant donné que la différence entre une fonction et une méthode indépendante est assez minime, Python 3 supprime la distinction; sous Python 3 accède à une fonction sur une instance de classe) vous donne juste la fonction elle-même:
>>> C.f1
<function f1 at 0x7fdd06c4cd40>
>>> C.f1 is f1
True
Dans les deux Python 2 et Python 3, alors, ces trois sont équivalents:
f1(C())
C.f1(C())
C().f1()
La liaison d'une fonction à une instance a pour effet de fixer son premier paramètre (conventionnellement appelé self
) à l'instance. Ainsi, la méthode liée C().f1
est équivalente à:
(lamdba *args, **kwargs: f1(C(), *args, **kwargs))
functools.partial(f1, C())
est assez difficile à comprendre
Eh bien, c'est un sujet assez difficile, et cela a à voir avec les descripteurs.
Commençons par la fonction. Tout est clair ici - vous l'appelez simplement, tous les arguments fournis sont passés lors de son exécution:
>>> f = A.__dict__['f1']
>>> f(1)
1
Un TypeError
régulier est levé en cas de problème avec le nombre de paramètres:
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f1() takes exactly 1 argument (0 given)
Maintenant, les méthodes. Les méthodes sont des fonctions avec un peu d'épices. Les descripteurs entrent en jeu ici. Comme décrit dans Modèle de données , A.f1
Et A().f1
sont traduits respectivement en A.__dict__['f1'].__get__(None, A)
et type(a).__dict__['f1'].__get__(a, type(a))
. Et les résultats de ces __get__
Diffèrent de la fonction brute f1
. Ces objets sont des wrappers autour du f1
D'origine et contiennent une logique supplémentaire.
Dans le cas de unbound method
Cette logique inclut une vérification si le premier argument est une instance 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 cette vérification réussit, elle exécute le f1
Original avec cette instance comme premier argument:
>>> f(A())
<__main__.A object at 0x800f238d0>
Notez que l'attribut im_self
Est None
:
>>> f.im_self is None
True
Dans le cas de bound method
, Cette logique fournit immédiatement f1
D'origine avec une instance de A
dont elle a été créée (cette instance est en fait stockée dans l'attribut im_self
):
>>> f = A().f1
>>> f.im_self
<__main__.A object at 0x800f23950>
>>> f()
<__main__.A object at 0x800f23950>
Ainsi, bound
signifie que la fonction sous-jacente est liée à une instance. unbound
signifie qu'il est toujours lié, mais uniquement à une classe.
L'objet fonction est un objet appelable créé par une définition de fonction. Les méthodes liées et non liées sont des objets appelables créés par un descripteur appelé par l'opérateur binaire point.
Les objets de méthode liés et non liés ont 3 propriétés principales: im_func
est l'objet fonction défini dans la classe, im_class
est la classe et im_self
est l'instance de classe. Pour les méthodes non liées, im_self
est None
.
Lorsqu'une méthode liée est appelée, elle appelle im_func
avec im_self
car le premier paramètre a suivi ses paramètres d'appel. les méthodes non liées appellent la fonction sous-jacente avec seulement ses paramètres d'appel.
Une chose intéressante que j'ai vue aujourd'hui est que, lorsque j'affecte une fonction à un membre de la classe, elle devient une méthode non liée. Tel que:
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")
Veuillez consulter la documentation Python 2 et Python pour plus de détails.
Mon interprétation est la suivante.
Extraits de classe Function
:
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)
Si une fonction est appelée à partir d'une classe ou d'une instance, son __get__
Est appelé pour récupérer la fonction encapsulée:
une. B.x
Est identique à B.__dict__['x'].__get__(None, B)
. En Python 3, cela renvoie une fonction simple. En Python 2, cela renvoie une fonction non liée.
b. b.x
Est identique à type(b).__dict__['x'].__get__(b, type(b)
. Cela renverra une méthode liée dans les deux Python 2 et Python 3, ce qui signifie que self
sera implicitement passé comme premier argument).
Quelle est la différence entre une fonction, une méthode non liée et une méthode liée?
Du point de vue révolutionnaire ce qui est une fonction il n'y a pas de différence. Python les fonctionnalités orientées objet sont construites sur un environnement basé sur les fonctions.
Être lié est égal à:
Voici l'exemple:
class C:
#instance method
def m1(self, x):
print(f"Excellent m1 self {self} {x}")
@classmethod
def m2(cls, x):
print(f"Excellent m2 cls {cls} {x}")
@staticmethod
def m3(x):
print(f"Excellent m3 static {x}")
ci=C()
ci.m1(1)
ci.m2(2)
ci.m3(3)
print(ci.m1)
print(ci.m2)
print(ci.m3)
print(C.m1)
print(C.m2)
print(C.m3)
Les sorties:
Excellent m1 self <__main__.C object at 0x000001AF40319160> 1
Excellent m2 cls <class '__main__.C'> 2
Excellent m3 static 3
<bound method C.m1 of <__main__.C object at 0x000001AF40319160>>
<bound method C.m2 of <class '__main__.C'>>
<function C.m3 at 0x000001AF4023CBF8>
<function C.m1 at 0x000001AF402FBB70>
<bound method C.m2 of <class '__main__.C'>>
<function C.m3 at 0x000001AF4023CBF8>
La sortie affiche la fonction statique m3
ne sera jamais appelé lié. C.m2
est lié à la classe C
car nous avons envoyé le paramètre cls
qui est le pointeur de classe.
ci.m1
et ci.m2
sont tous deux liés; ci.m1
car nous avons envoyé self
qui est un pointeur vers l'instance, et ci.m2
car l'instance sait que la classe est liée;).
Notez que la méthode peut ne pas faire partie à l'origine de la classe. Vérifiez cette réponse d'Alex Martelli pour plus de détails.