31 votes

Comment Python distingue-t-il la fonction callback qui est membre d'une classe ?

Regardez cet exemple simple :

class A:
  def __init__(self, flag):
    self.flag = flag

  def func(self):
    print self.flag

a = A(1)
b = A(2)
callback_a = a.func
callback_b = b.func

callback_a()
callback_b()

Le résultat est :

1
2

Il fonctionne comme prévu. Mais j'ai une question. En C, la fonction de rappel est passée comme un pointeur. En Python, il devrait y avoir un moyen similaire de le faire, de sorte que l'appelant connaisse l'adresse de la fonction. Mais dans mon exemple, ce n'est pas seulement le pointeur de fonction qui est passé, mais aussi le paramètre (self), car la même méthode de la même classe donne des résultats différents. Mes questions sont donc les suivantes :

  1. Est-ce qu'une telle méthode en Python n'a qu'une seule copie en mémoire ? Je veux dire que le code d'une méthode n'a qu'une seule copie, et dans mon exemple la méthode ne sera pas clonée elle-même. Je pense qu'elle ne devrait avoir qu'une seule copie, mais je pose quand même cette question afin d'obtenir plus d'informations.

  2. Je me souviens que tout en Python est un objet. Donc dans mon exemple, y a-t-il deux instances de fonction avec des paramètres différents mais une seule copie du code ?

24voto

Greg Hewgill Points 356191

En Python, le callback n'est pas simplement une référence à une fonction membre. Au lieu de cela, il est "lié" à l'objet auquel il fait référence lors de sa création. Ainsi, a.func crée un appelant qui est lié à a y b.func crée un appelant qui est lié à b .

Python n'a besoin que d'une seule implémentation de func() en mémoire, mais il créera probablement une ou plusieurs fonctions "trampoline" à l'exécution pour accomplir la liaison (je ne suis pas certain des détails internes à ce sujet, et cela différerait entre les implémentations Python de toute façon).

Si vous imprimez id(callback_a) y id(callback_b) vous obtiendrez des résultats différents, montrant qu'il s'agit bien d'objets appelables différents.

23voto

aaronasterling Points 25749

Spécifique à CPython, il n'y a qu'une seule copie de l'objet fonction. Lors de la création de l'instance, la classe enveloppe les fonctions non liées de son espace de noms en tant que méthodes liées. Mais elles enveloppent toutes la même fonction.

Voici votre exemple élargi pour montrer ce qui se passe.

class A(object):
  def __init__(self, flag):
    self.flag = flag

  def func(self):
    print self.flag

a = A(1)
b = A(2)

callback_a = a.func
callback_b = b.func

print "typeof(callback_a) = {0}".format(type(callback_a))
print "typeof(callback_b) = {0}".format(type(callback_b))

print "typeof(callback_a.__func__) = {0}".format(type(callback_a.__func__))
print "typeof(callback_b.__func__) = {0}".format(type(callback_b.__func__))

print "'callback_a.__func__ is callback_b.__func__'  is {0}".format(callback_a.__func__ is callback_b.__func__)

callback_a()
callback_b()

Ce code produit

typeof(callback_a) = <type 'instancemethod'>
typeof(callback_b) = <type 'instancemethod'>
typeof(callback_a.__func__) = <type 'function'>
typeof(callback_b.__func__) = <type 'function'>
'callback_a.__func__ is callback_b.__func__'  is True

Vous pouvez clairement voir, en utilisant le is que les deux instancemethod partagent le même objet fonction.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X