web-dev-qa-db-fra.com

Différence entre méthodes et fonctions, en Python par rapport à C ++

Je fais les tutoriels de Code Academy sur Python, et je suis un peu confus quant à la définition de la méthode et de la fonction. Du tutoriel:

Vous connaissez déjà certaines des fonctions intégrées que nous avons utilisées sur (ou pour créer) des chaînes, telles que .upper(), .lower(), str() et len().

Venant de C++, je penserais que .upper() et .lower() seraient appelées méthodes et len() et str() fonctions. Dans le didacticiel, les termes semblent être utilisés de manière interchangeable.

Python fait-il la distinction entre les méthodes et les fonctions comme le fait C++?

Contrairement à Différence entre une méthode et une fonction , je pose des questions sur les particularités de Python. Les termes "méthode" et "fonction" ne semblent pas toujours suivre la définition donnée dans la réponse acceptée à la question liée.

49
Q-bertsuit

Un fonction est un objet appelable en Python, c'est-à-dire qu'il peut être appelé à l'aide de opérateur d'appel (bien que d'autres objets puissent émuler une fonction en implémentant __call__). Par exemple:

>>> def a(): pass
>>> a
<function a at 0x107063aa0>
>>> type(a)
<type 'function'>

Une méthode est une classe spéciale de fonction, qui peut être liée ou non liée.

>>> class A:
...   def a(self): pass
>>> A.a
<unbound method A.a>
>>> type(A.a)
<type 'instancemethod'>

>>> A().a
<bound method A.a of <__main__.A instance at 0x107070d88>>
>>> type(A().a)
<type 'instancemethod'>

Bien sûr, une méthode non liée ne peut pas être appelée (du moins pas directement sans passer une instance en argument):

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

En Python, dans la plupart des cas, vous ne remarquerez pas la différence entre une méthode liée, une fonction ou un objet appelable (c'est-à-dire un objet qui implémente __call__), Ou un constructeur de classe. Ils se ressemblent tous, ils ont juste des conventions d'appellation différentes. Sous le capot, les objets peuvent cependant être très différents.

Cela signifie qu'une méthode liée peut être utilisée en tant que fonction, c'est l'une des nombreuses petites choses qui rendent Python si puissant

>>> b = A().a
>>> b()

Cela signifie également que même s'il existe une différence fondamentale entre len(...) et str(...) (ce dernier est un constructeur de type), vous ne remarquerez pas la différence avant de creuser un peu plus profondément:

>>> len
<built-in function len>
>>> str
<type 'str'>
49
Krumelur

Si vous ne comprenez toujours pas comment fonctionnent les méthodes, un examen de la mise en œuvre peut peut-être clarifier les choses. Lorsqu'un attribut d'instance est référencé qui n'est pas un attribut de données, sa classe est recherchée. Si le nom dénote un attribut de classe valide qui est un objet fonction, un objet méthode est créé en empaquetant (pointeurs vers) l'objet instance et l'objet fonction que l'on vient de trouver ensemble dans un objet abstrait: c'est l'objet méthode. Lorsque l'objet méthode est appelé avec une liste d'arguments, une nouvelle liste d'arguments est construite à partir de l'objet instance et de la liste d'arguments, et l'objet fonction est appelé avec cette nouvelle liste d'arguments.

http://docs.python.org/2/tutorial/classes.html#method-objects

Lisez attentivement cet extrait.

Ça veut dire :

1) Une instance ne contient pas vraiment un objet étant une méthode qui serait son attribut.
En fait, il n'y a pas du tout d'attribut "méthode" dans le __dict__ D'une instance (__dict__ Est l'espace de nom d'un objet)

2) Le fait qu'une instance semble avoir une "méthode" lorsqu'un attribut "méthode" est appelé, est dû à un processus, et non à la présence d'un objet méthode à l'intérieur de l'espace de noms d'une instance

3) De plus, il n'existe pas vraiment d'objet méthode dans l'espace de noms d'une classe.

Mais il y a une différence avec une instance, car il doit y avoir quelque part quelque chose qui mène à un véritable objet méthode lorsqu'un tel appel est fait, n'est-ce pas?

Ce qui est appelé un attribut "méthode" d'une classe, pour faciliter la formulation, est en réalité un fonction objet en cours d'attribution dans l'espace de noms de la classe.
C'est-à-dire qu'une paire (identifiant de la fonction, fonction) est membre du __dict__ D'une classe, et cet attribut permet l'interprète pour construire un objet de méthode lors d'un appel de méthode.

4) Encore une fois, le fait qu'une classe semble avoir une "méthode" lorsqu'un attribut "méthode" est appelé, est dû à un processus, pas à la présence d'un objet méthode à l'intérieur de l'espace de noms d'une classe

[~ # ~] modifier [~ # ~] Je n'en suis pas plus sûr; voir à la fin

5) Un objet méthode (pas un objet "méthode"; je veux dire que le véritable objet étant vraiment une méthode`, ce qui est décrit dans l'extrait) est créé au moment de l'appel, il n'existe pas auparavant.
C'est une sorte de wrapper: il regroupe des pointeurs vers l'objet instance et l'objet fonction sur lequel la méthode est basée.

Ainsi, une méthode est basée sur une fonction. Cette fonction est pour moi le véritable attribut de la classe détenant ladite "méthode", car cette fonction appartient vraiment à l'espace de noms (__dict__) De la classe: cette fonction est décrite comme un <function ......> lorsque le __dict__ est imprimé.
Cette fonction est accessible depuis l'objet méthode en utilisant l'alias im_func Ou __func__ (Voir le code ci-dessous)

.

Je pense que ces notions ne sont pas très connues et comprises. Mais le code suivant prouve ce que j'ai dit.

class A(object):
    def __init__(self,b=0):
        self.b = b
    print 'The __init__ object :\n',__init__

    def addu(self):
        self.b = self.b + 10
    print '\nThe addu object :\n',addu


print '\nThe A.__dict__  items :\n',
print '\n'.join('  {0:{align}11}  :  {1}'.format(*it,align='^')
                for it in A.__dict__.items())
a1 = A(101)
a2 = A(2002)

print '\nThe a1.__dict__  items:'
print '\n'.join('  {0:{align}11}  :  {1}'.format(*it,align='^')
                for it in a1.__dict__.items())

print '\nThe a2.__dict__  items:'
print '\n'.join('  {0:{align}11}  :  {1}'.format(*it,align='^')
                for it in a2.__dict__.items())

print '\nA.addu.__func__ :',A.addu.__func__
print id(A.addu.__func__),'==',hex(id(A.addu.__func__))
print

print 'A.addu :\n  ',
print A.addu,'\n  ',id(A.addu),'==',hex(id(A.addu))

print 'a1.addu :\n  ',
print a1.addu,'\n  ',id(a1.addu),'==',hex(id(a1.addu))
print 'a2.addu :\n  ',
print a2.addu,'\n  ',id(a2.addu),'==',hex(id(a2.addu))

a2.addu()
print '\na2.b ==',a2.b

print '\nThe A.__dict__  items :\n',
print '\n'.join('  {0:{align}11}  :  {1}'.format(*it,align='^')
                for it in A.__dict__.items())

résultat

The __init__ object :
<function __init__ at 0x011E54B0>

The addu object :
<function addu at 0x011E54F0>

The A.__dict__  items :
  __module__   :  __main__
     addu      :  <function addu at 0x011E54F0>
   __dict__    :  <attribute '__dict__' of 'A' objects>
  __weakref__  :  <attribute '__weakref__' of 'A' objects>
    __doc__    :  None
   __init__    :  <function __init__ at 0x011E54B0>

The a1.__dict__  items:
       b       :  101

The a2.__dict__  items:
       b       :  2002

A.addu.__func__ : <function addu at 0x011E54F0>
18765040 == 0x11e54f0

A.addu :
   <unbound method A.addu> 
   18668040 == 0x11cda08
a1.addu :
   <bound method A.addu of <__main__.A object at 0x00CAA850>> 
   18668040 == 0x11cda08
a2.addu :
   <bound method A.addu of <__main__.A object at 0x011E2B90>> 
   18668040 == 0x11cda08

a2.b == 2012

The A.__dict__  items :
  __module__   :  __main__
     addu      :  <function addu at 0x011E54F0>
   __dict__    :  <attribute '__dict__' of 'A' objects>
  __weakref__  :  <attribute '__weakref__' of 'A' objects>
    __doc__    :  None
   __init__    :  <function __init__ at 0x011E54B0>

.

MODIFIER

Quelque chose me dérange et je ne connais pas les entrailles profondes du sujet:

Le code ci-dessus montre que A.addu, a1.addu Et a2.addu Sont tous le même objet de méthode, avec une identité unique.
Cependant, A.addu Est considéré comme une méthode non liée car il ne contient aucune information concernant une instance particulière,
et a1.addu et a2.addu sont des méthodes liées car chacune possède des informations désignant l'instance qui doit être concernée par les opérations de la méthode.
Logiquement, pour moi, cela signifierait que la méthode devrait être différente pour chacun de ces 3 cas.

[~ # ~] mais [~ # ~] l'identité est la même pour les trois, et d'ailleurs cette identité est différente de l'identité de la fonction sur laquelle repose la méthode.
Cela conduit à la conclusion que la méthode est vraiment un objet vivant dans la mémoire, et qu'elle ne change pas d'un appel d'une instance à un autre cal d'une autre instance.

[~ # ~] cependant [~ # ~] , en imprimant l'espace de noms __dict__ de la classe, même après la création des instances et l'appel de la "méthode" addu(), cet espace de noms n'expose pas un nouvel objet qui pourrait être identifié à l'objet méthode différent de la fonction addu.

Qu'est-ce que ça veut dire ?
Cela me donne l'impression que dès qu'un objet méthode est créé, il n'est pas détruit, il vit dans la mémoire (RAM).
Mais il vit caché et seuls les processus qui forment le fonctionnement de l'interpeter savent comment et où le trouver.
Cet objet caché, l'objet de méthode réel, doit avoir la capacité de changer la référence à l'instance à laquelle la fonction doit être appliquée, ou de référencer None s'il est appelé comme méthode non liée . C'est ce qu'il me semble, mais ce n'est qu'une hypothèse de brain-storming.

Quelqu'un sait-il quelque chose sur cet interrogatoire?


Pour répondre à la question, il peut être considéré comme correct d'appeler .upper Et .lower fonctions, car en réalité ils sont basés sur des fonctions comme chaque méthode d'une classe .

Cependant, le résultat suivant est spécial, probablement parce qu'il s'agit de méthodes/fonctions intégrées, et non de méthodes/fonctions d'utilisateur comme dans mon code.

x = 'hello'
print x.upper.__func__

résultat

    print x.upper.__func__
AttributeError: 'builtin_function_or_method' object has no attribute '__func__'
4
eyquem

Fondamentalement, oui, Python les distingue, mais dans Python il est courant de voir les méthodes comme un sous-ensemble de fonctions. Les méthodes sont associées à une classe ou une instance) et les "fonctions autonomes" ne le sont pas. Quelque chose qui est une méthode est aussi une fonction, mais il peut y avoir des fonctions qui ne sont pas des méthodes.

Comme Jon Clements l'a mentionné dans son commentaire, cependant, la distinction n'est pas aussi évidente qu'en C++. Les fonctions autonomes peuvent être "converties" en méthodes au moment de l'exécution, et les méthodes peuvent être affectées à des variables de manière à ce qu'elles ne se comportent effectivement pas différemment des fonctions autonomes. La frontière entre méthodes et fonctions est donc perméable.

2
BrenBarn

Dans la définition de classe suivante:

class MyClass:
    """A simple example class"""
    def f(self):
        return 'hello world'
  • Classe: MyClass
  • Fonction: f ()
  • Méthode: Aucune (En fait, sans objet)

Permet de créer une instance de la classe ci-dessus. Nous allons le faire en affectant class object, i.e. MyClass() à var x

  x = MyClass()

Ici,

  • Fonction: Aucun
  • Méthode: x.f ()

Et n'oublions pas que le function object MyClass.f A été utilisé pour définir (en interne) le method object x.f Lorsque nous avons affecté x à MyClass ()

2
Ketan