2009-10-19 16 views
63

Quali benefici o implicazioni potremmo ottenere con codice Python come questo:funzione annidata in Python

class some_class(parent_class): 
    def doOp(self, x, y): 
     def add(x, y): 
      return x + y 
     return add(x, y) 

Ho trovato questo in un progetto open-source, fare qualcosa di utile all'interno della funzione annidata, ma facendo assolutamente nulla al di fuori tranne che chiamarlo. (È possibile trovare il codice attuale here). Perché qualcuno potrebbe codificarlo in questo modo? C'è qualche vantaggio o effetto collaterale per scrivere il codice all'interno della funzione nidificata piuttosto che nella funzione normale esterna?

+4

E 'questo il codice vero e proprio che hai trovato, o semplicemente un esempio semplificato si costruisce? – MAK

+0

È un esempio semplificato. Il codice attuale può essere trovato qui: http://bazaar.launchpad.net/%7Eopenerp/openobject-server/trunk/annotate/head%3A/bin/report/render/rml2pdf/trml2pdf.py#L685 –

+2

Il tuo link (che punta a HEAD) ora non è preciso. Prova: http://bazaar.launchpad.net/~openerp/openobject-server/trunk/annotate/1861/bin/report/render/rml2pdf/trml2pdf.py#L685 –

risposta

85

Normalmente lo si fa a fare closures:

def make_adder(x): 
    def add(y): 
     return x + y 
    return add 

plus5 = make_adder(5) 
print(plus5(12)) # prints 17 

funzioni interne possono accedere alle variabili dal campo di applicazione che racchiude (in questo caso, la variabile locale x) . Se non si sta accedendo a nessuna variabile dall'ambito di inclusione, sono in realtà solo normali funzioni con un ambito diverso.

+4

Per questo preferirei partial: 'plus5 = functools.partial (operator.add, 5)'. I decoratori sarebbero un esempio migliore per chiusure. –

+3

Grazie, ma come puoi vedere nello snippet che ho postato, non è questo il caso: la funzione nidificata viene semplicemente chiamata nella funzione esterna. –

8

Non riesco a immaginare nessuna buona ragione per un codice del genere.

Forse c'era una ragione per la funzione interna nelle revisioni precedenti, come le altre Ops.

Ad esempio, questo rende leggermente più senso:

class some_class(parent_class): 
    def doOp(self, op, x, y): 
     def add(x, y): 
      return x + y 
     def sub(x,y): 
      return x - y 
     return locals()[op](x,y) 

some_class().doOp('add', 1,2) 

ma poi la funzione interna dovrebbe essere ("private") metodi di classe invece:

class some_class(object): 
    def _add(self, x, y): 
     return x + y 
    def doOp(self, x, y): 
     return self._add(x,y) 
+0

Sì, forse doOp ha preso una stringa per specificare quale operatore utilizzare sugli argomenti ... – Skilldrick

+4

non dovresti usare le classi in python semplicemente per organizzare le funzioni ... – aehlke

+0

@aehlke - perché no? – ArtOfWarfare

6

L'idea alla base di metodi locali è simile alle variabili locali: non inquinare lo spazio dei nomi più grande. Ovviamente i vantaggi sono limitati poiché la maggior parte delle lingue non fornisce direttamente tali funzionalità.

1

Sei sicuro che il codice fosse esattamente come questo? La normale ragione per fare qualcosa di simile è creare un partial - una funzione con parametri integrati. Chiamare la funzione esterna restituisce un callable che non ha bisogno di parametri e quindi può quindi essere memorizzato e utilizzato da qualche parte è impossibile passare i parametri. Tuttavia, il codice che hai pubblicato non lo farà: chiama immediatamente la funzione e restituisce il risultato, piuttosto che il chiamabile. Potrebbe essere utile pubblicare il codice reale che hai visto.

+1

Ho aggiunto un collegamento al codice originale in un commento sulla mia domanda. Come puoi vedere, il mio è un esempio semplificato, ma è quasi sempre lo stesso. –

44

Oltre ai generatori di funzioni, in cui la creazione di funzioni interne è quasi la definizione di un generatore di funzioni, la ragione per cui creo le funzioni nidificate è il miglioramento della leggibilità. Se ho una funzione minuscola che verrà invocata solo dalla funzione esterna, allora io inline la definizione in modo da non dover saltare attorno per determinare cosa sta facendo quella funzione. Posso sempre spostare il metodo interno al di fuori del metodo di incapsulamento se trovo la necessità di riutilizzare la funzione in un secondo momento. esempio

Toy:

import sys 

def Foo(): 
    def e(s): 
     sys.stderr.write('ERROR: ') 
     sys.stderr.write(s) 
     sys.stderr.write('\n') 
    e('I regret to inform you') 
    e('that a shameful thing has happened.') 
    e('Thus, I must issue this desultory message') 
    e('across numerous lines.') 
Foo() 
+2

L'unico problema è che a volte questo può rendere più difficile il test dell'unità, poiché non è possibile accedere alla funzione interna. – Challenger5

+1

Sono così adorabili! – taper

21

Uno dei potenziali vantaggi dell'utilizzo di metodi interni è che consente di utilizzare variabili locali del metodo esterno senza passarle come argomenti.

def helper(feature, resultBuffer): 
    resultBuffer.print(feature) 
    resultBuffer.printLine() 
    resultBuffer.flush() 

def save(item, resultBuffer): 

    helper(item.description, resultBuffer) 
    helper(item.size, resultBuffer) 
    helper(item.type, resultBuffer) 

può essere scritta come segue, in cui si legge forse meglio

def save(item, resultBuffer): 

    def helper(feature): 
    resultBuffer.print(feature) 
    resultBuffer.printLine() 
    resultBuffer.flush() 

    helper(item.description) 
    helper(item.size) 
    helper(item.type)