Differenza tra metodi e funzioni, in Python rispetto a C ++

Sto facendo le esercitazioni di Code Academy su Python e sono un po ‘confuso riguardo alla definizione di metodo e funzione. Dal tutorial:

Conoscete già alcune delle funzioni built-in che abbiamo usato (o per creare) stringhe, come .upper() , .lower() , str() e len() .

Provenendo da C ++, .upper() che .upper() e .lower() sarebbero chiamate metodi e funzioni len() e str() . Nel tutorial, i termini sembrano essere usati in modo intercambiabile.

Python distingue tra metodi e funzioni come fa C ++?

A differenza della differenza tra un metodo e una funzione , sto chiedendo i particolari di Python. I termini “metodo” e “funzione” non sembrano seguire sempre la definizione data nella risposta accettata della domanda collegata.

Una funzione è un object callable in Python, cioè può essere chiamata usando l’ operatore di chiamata (anche se altri oggetti possono emulare una funzione implementando __call__ ). Per esempio:

 >>> def a(): pass >>> a  >>> type(a)  

Un metodo è una class speciale di funzioni, una che può essere associata o non associata .

 >>> class A: ... def a(self): pass >>> Aa  >>> type(Aa)  >>> A().a > >>> type(A().a)  

Ovviamente, un metodo non associato non può essere chiamato (almeno non direttamente senza passare un’istanza come argomento):

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

In Python, nella maggior parte dei casi non noterai la differenza tra un metodo associato, una funzione o un object chiamabile (cioè un object che implementa __call__ ) o un costruttore di classi. Sembrano tutti uguali, hanno solo convenzioni di denominazione diverse. Sotto il cofano, gli oggetti possono sembrare molto diversi però.

Ciò significa che un metodo associato può essere usato come una funzione, questa è una delle tante piccole cose che rendono Python così potente

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

Significa anche che anche se c’è una differenza fondamentale tra len(...) e str(...) (quest’ultimo è un costruttore di tipi), non noterai la differenza finché non approfondirai un po ‘:

 >>> len  >>> str  

Se ancora non capisci come funzionano i metodi, uno sguardo all’implementazione può forse chiarire le cose. Quando si fa riferimento a un attributo di istanza che non è un attributo di dati, la sua class viene cercata. Se il nome denota un attributo di class valido che è un object funzione, viene creato un object metodo comprimendo (puntatori) l’object istanza e l’object funzione appena trovato insieme in un object astratto: questo è l’object metodo. Quando l’object metodo viene chiamato con un elenco di argomenti, viene creato un nuovo elenco di argomenti dall’object istanza e dall’elenco di argomenti e l’object funzione viene chiamato con questo nuovo elenco di argomenti.

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

Leggi attentamente questo estratto.

Significa :

1) Un’istanza in realtà non ritiene che un object sia un metodo che sarebbe il suo attributo.
In realtà, non esiste affatto un attributo “metodo” nel __dict__ di un’istanza ( __dict__ è lo spazio dei nomi di un object)

2) Il fatto che un’istanza abbia un “metodo” quando viene chiamato un attributo “metodo” è dovuto a un processo, non alla presenza di un object metodo all’interno dello spazio dei nomi di un’istanza

3) Inoltre, non esiste realmente un object metodo nello spazio dei nomi di una class.

Ma c’è una differenza con un’istanza, perché deve esserci qualcosa che conduce a un object metodo reale quando viene effettuata una tale chiamata, non è così?

Quello che viene chiamato un attributo “metodo” di una class, per facilità di espressione, è in realtà un object funzione che si attribuisce nello spazio dei nomi della class.
Vale a dire, una coppia (identificatore della funzione, funzione) è un membro del __dict__ di una class, e questo attributo consente all’Inprete di build un object metodo quando viene eseguita una chiamata al metodo.

4) Ancora, il fatto che una class sembri avere un “metodo” quando viene chiamato un attributo “metodo”, è dovuto a un processo, non alla presenza di un object metodo all’interno dello spazio dei nomi di una class

EDIT Non ne sono più sicuro; vedere alla fine

5) Un object metodo (non un object “metodo”, intendo che l’object reale è in realtà un metodo “, ciò che è descritto nell’estratto) viene creato al momento della chiamata, non esiste prima.
È una specie di wrapper: racchiude i puntatori all’object istanza e all’object funzione su cui si basa il metodo.

Quindi, un metodo è basato su una funzione. Questa funzione è per me il vero attributo della class che detiene il detto “metodo”, perché questa funzione appartiene veramente allo spazio dei nomi ( __dict__ ) della class: questa funzione è descritta come una quando il __dict__ è stampato.
Questa funzione può essere raggiunta dall’object metodo usando l’alias im_func o __func__ (vedi il codice sottostante)

.

Credo che queste nozioni non siano molto conosciute e comprese. Ma il codice seguente dimostra quello che ho detto.

 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()) 

risultato

 The __init__ object :  The addu object :  The A.__dict__ items : __module__ : __main__ addu :  __dict__ :  __weakref__ :  __doc__ : None __init__ :  The a1.__dict__ items: b : 101 The a2.__dict__ items: b : 2002 A.addu.__func__ :  18765040 == 0x11e54f0 A.addu :  18668040 == 0x11cda08 a1.addu : > 18668040 == 0x11cda08 a2.addu : > 18668040 == 0x11cda08 a2.b == 2012 The A.__dict__ items : __module__ : __main__ addu :  __dict__ :  __weakref__ :  __doc__ : None __init__ :  

.

MODIFICARE

Qualcosa mi preoccupa e non conosco le viscere profonde del sobject:

Il codice sopra mostra che A.addu , a1.addu e a2.addu sono tutti lo stesso object metodo, con un’id quadro univoca.
Comunque A.addu è detto un metodo non A.addu perché non ha alcuna informazione riguardante una particolare istanza,
e a1.addu e a2.addu sono detti metodi associati perché ognuno ha informazioni che designano l’istanza che deve essere interessata dalle operazioni del metodo.
Logicamente, per me, ciò significherebbe che il metodo dovrebbe essere diverso per ognuno di questi 3 casi.

MA l’identity framework è la stessa per tutti e tre, e inoltre questa id quadro è diversa dall’id quadro della funzione su cui si basa il metodo.
Porta alla conclusione che il metodo è in realtà un object che vive nella memoria e che non cambia da una chiamata da un’istanza a un’altra cal da un’altra istanza.

TUTTAVIA , stampando lo spazio dei nomi __dict__ della class, anche dopo la creazione delle istanze e la chiamata del “metodo” addu() , questo spazio dei nomi non espone un nuovo object che potrebbe essere identificato all’object metodo diverso dalla funzione addu .

Cosa significa ?
Mi dà l’impressione che non appena un object metodo viene creato, non viene distrutto, vive nella memoria (RAM).
Ma vive nascosto e solo i processi che formano il funzionamento dell’interpeter sanno come e dove trovarlo.
Questo object nascosto, l’object metodo reale, deve avere la possibilità di modificare il riferimento all’istanza a cui deve essere applicata la funzione o fare riferimento a None se viene chiamato come metodo non associato. Questo è quello che mi sembra, ma è solo un’ipotesi di brain storming.

Qualcuno sa qualcosa su questo interrogatorio?


Per rispondere alla domanda, si può ritenere corretto chiamare le funzioni .upper e .lower , poiché in realtà sono basate su funzioni come ogni metodo di una class.

Tuttavia, il seguente risultato è speciale, probabilmente perché sono metodi / funzioni incorporati, non metodi / funzioni dell’utente come nel mio codice.

 x = 'hello' print x.upper.__func__ 

risultato

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

Fondamentalmente, sì, Python li distingue, ma in Python è comune visualizzare i metodi come un sottoinsieme di funzioni. I metodi sono associati a una class o un’istanza e le “funzioni standalone” non lo sono. Qualcosa che è un metodo è anche una funzione, ma possono esserci funzioni che non sono metodi.

Come ha detto Jon Clements nel suo commento, però, la distinzione non è così ironica come in C ++. Le funzioni standalone possono essere “convertite” in metodi in fase di esecuzione e i metodi possono essere assegnati alle variabili in modo tale da non comportarsi in modo efficace rispetto alle funzioni autonome. Quindi il confine tra i metodi e le funzioni è permeabile.

Nella seguente definizione di class:

 class MyClass: """A simple example class""" def f(self): return 'hello world' 
  • Classe : MyClass
  • Funzione : f ()
  • Metodo : Nessuno (In realtà, non applicabile)

Consente di creare un’istanza della class precedente. Lo faremo assegnando un class object, ie MyClass() a var x

  x = MyClass() 

Qui,

  • Funzione : Nessuna
  • Metodo : xf ()

E non dimentichiamo che l’ function object MyClass.f stato usato per definire (internamente) l’ method object xf quando abbiamo assegnato x a MyClass ()