2009-10-19 17 views
63

Jakie korzyści lub skutki możemy uzyskać z kodem Pythona tak:zagnieżdżonych funkcji w Pythonie

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

Znalazłem to w projekcie open source, robiąc coś pożytecznego wewnątrz funkcji zagnieżdżonych, ale robi absolutnie nic, poza oprócz wywoływania go. (Rzeczywisty kod można znaleźć here.) Dlaczego ktoś może to tak kodować? Czy istnieje jakaś korzyść lub skutek uboczny przy pisaniu kodu wewnątrz zagnieżdżonej funkcji, a nie w zewnętrznej, normalnej funkcji?

+4

Czy to rzeczywisty kod znalazłeś, lub po prostu uproszczony przykład skonstruowano? – MAK

+0

Jest to uproszczony przykład. Aktualny kod można znaleźć tutaj: http://bazaar.launchpad.net/%7Eopenerp/openobject-server/trunk/annotate/head%3A/bin/report/render/rml2pdf/trml2pdf.py#L685 –

+2

Twój link (który wskazuje HEAD) nie jest teraz dokładny. Spróbuj: http://bazaar.launchpad.net/~openerp/openobject-server/trunk/annotate/1861/bin/report/render/rml2pdf/trml2pdf.py#L685 –

Odpowiedz

85

Normalnie to zrobić, aby closures:

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

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

funkcje wewnętrzne mogą uzyskać dostęp do zmiennych zamykającego (w tym przypadku zmienna lokalna x) . Jeśli nie uzyskujesz dostępu do żadnych zmiennych z otaczającego zakresu, to są to zwykłe funkcje o innym zasięgu.

+4

W tym celu wolę partials: 'plus5 = functools.partial (operator.add, 5)'. Dekoratorzy będą lepszym przykładem dla zamknięć. –

+3

Dzięki, ale jak widać w zamieszczonym fragmencie, nie ma to miejsca w tym przypadku: funkcja zagnieżdżona jest po prostu wywoływana w funkcji zewnętrznej. –

8

Nie mogę podać żadnego dobrego powodu dla takiego kodu.

Być może był powód funkcji wewnętrznej w starszych wersjach, tak jak inne operacje.

Na przykład, to sprawia nieznacznie więcej sensu:

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) 

ale wtedy funkcja wewnętrzna powinna być ("prywatny") metody klasy Zamiast:

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

Tak, może zrobićOp wziął ciąg, aby określić, który operator użyć na argumenty ... – Skilldrick

+4

nie powinieneś używać klas w Pythonie po prostu do organizowania funkcji ... – aehlke

+0

@aehlke - dlaczego nie? – ArtOfWarfare

6

Ideą metod lokalnych jest podobne do zmiennych lokalnych: nie zanieczyszczaj większej przestrzeni nazw. Oczywiście korzyści są ograniczone, ponieważ większość języków nie zapewnia tej funkcji bezpośrednio.

1

Czy na pewno kod był dokładnie taki? Normalnym powodem robienia czegoś takiego jest tworzenie częściowego - funkcji z parametrami zapieczenia. Wywołanie funkcji zewnętrznej powoduje wywołanie wywołania, które nie wymaga parametrów, a zatem może być przechowywane i używane gdzieś, gdzie nie można przekazać parametrów. Jednak wysłany przez ciebie kod nie spowoduje tego - natychmiast wywoła funkcję i zwróci wynik, a nie wywołanie. Może być przydatne opublikowanie rzeczywistego kodu, który zobaczyłeś.

+1

Dodałem link do oryginalnego kodu w komentarzu do mojego pytania. Jak widać, mój jest uproszczonym przykładem, ale wciąż jest prawie taki sam. –

44

Oprócz generatorów funkcji, w których tworzenie funkcji wewnętrznych jest prawie definicją generatora funkcji, przyczyną tworzenia funkcji zagnieżdżonych jest poprawa czytelności. Jeśli mam małą funkcję, która będzie wywoływana tylko przez funkcję zewnętrzną, wówczas wstawiam definicję, aby nie musieć przeskakiwać wokół, aby ustalić, co ta funkcja wykonuje. Zawsze mogę przenieść metodę wewnętrzną poza metodę enkapsulacji, jeśli znajdę potrzebę ponownego użycia tej funkcji w późniejszym terminie. Przykładem

Zabawka:

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

Jedynym problemem jest to, że czasami może to utrudnić test jednostkowy, ponieważ nie można uzyskać dostępu do funkcji wewnętrznej. – Challenger5

+1

Wyglądają tak pięknie! – taper

21

Jedną z potencjalnych korzyści wynikających z zastosowania metod wewnętrznych jest to, że umożliwia stosowanie zmiennych lokalnych metody zewnętrznej bez przekazywania ich jako argumentów.

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) 

można zapisać w następujący sposób, który zapewne brzmi lepiej

def save(item, resultBuffer): 

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

    helper(item.description) 
    helper(item.size) 
    helper(item.type) 
Powiązane problemy