Nested Functions

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
myxin
User
Beiträge: 47
Registriert: Dienstag 10. Januar 2012, 16:57

Hallo,

ich kenne/kannte das Prinzip der nested functions nicht.
Wohl habe ich verstanden wie dies zu programmieren wäre,
nur erschliesst sich mir nicht warum ich das tun sollte.
In Dive into Python wird zwar gesagt, dass nested functions nur aus der Funktion
heraus gestartet werden in welcher diese definiert sind, aber leider
wird nicht erklärt was der Vorteil/Zweck ist.

Kann es sein, dass man damit eine Art "private" Funktion deklarieren will?

Danke
Claudia
deets

Du paraphrasierst das Buch, darum weiss ich nicht genau, ob die das so sagen - aber so wie *du* das sagst, ist das falsch. Man ruft nested funktionen nicht notwendigerweise nur innerhalb der umgebenden Funktion auf.

Und Anwendungen sind vielfaeltig:

- als kleine Hilfsfunktionen in zB Praedikaten fuer sowas wie filter oder List-comprehensions.
- generell als code-schnipsel die fuer sich funktionieren, aber den globalen oder Klassennamensraum nicht "zumuellen" sollen
- sehr wichtig: als closure, um zB callbacks zu erzeugen, die aber impliziten Zustand haben. ZB fuer Dekoratoren aller Arten:

Code: Alles auswählen

def synchronized(f):
      def _w(self, *args, **kwargs):
            with self.lock:
                    return f(self, *args, **kwargs)
      return _w


class Foo(object):

    def __init__(self):
         self.lock = threading.Lock()


    @synchronized
    def mach_watt(self):
         print "boo!"

Und so weiter und so fort.
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Das Beispiel von deets ist eine Inkarnation hiervon: http://de.wikipedia.org/wiki/Closure.

Hier ein anderes Beispiel, bei dem allerdings die innere Funktion nicht außerhalb ihres umgebenden Kontexts verwendet wird:

Code: Alles auswählen

from collections import defaultdict

def connected_components(edges):
    neighbors = defaultdict(set)
    for a, b in edges:
        neighbors[a].add(b)
        neighbors[b].add(a)
    visited = set()
    def component(node):  #  <-- hier wird eine Funktion definiert, die Zugriff auf den sie umgebenden
        stack = [node]    #      Kontext hat, die aber nicht außerhalb dieses Kontexts verwendet wird.
        while stack:
            node = stack.pop()
            visited.add(node)
            yield node
            stack.extend(n for n in neighbors[node] if n not in visited)
    for node in neighbors:
        if node not in visited:
            yield set(component(node))  #  <-- hier wird die Funktion verwendet.

some_graph = [(1, 2), (1, 3), (2, 7), (4, 5), (4, 6), (5, 5), (6, 5)]

print list(connected_components(some_graph))
Das Ergebnis müsste dann ungefähr so aussehen:

Code: Alles auswählen

[set([1, 2, 3, 7]), set([4, 5, 6])]
Die Reihenfolge wird möglicherweise variieren.
In specifications, Murphy's Law supersedes Ohm's.
myxin
User
Beiträge: 47
Registriert: Dienstag 10. Januar 2012, 16:57

@deets, @pillmuncher,

vielen Dank für Eure Hilfe.
Kann man sagen, dass das primäre Ziel von nested functions der ist, diese im lokalen Kontext zu halten?

@deets - Wird bei deinem Beispiel nicht erst hier

Code: Alles auswählen

return _w
die Funktion aufgerufen?
Wenn ja, dann würde diese ja wieder erst in der sie umgebenden Funktion synchronized aufgerufen, oder?

Danke
Claudia
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@myxin: Vielleicht wird es dadurch klarer:

Code: Alles auswählen

def add_n(n):
    def adder(m):
        print 'nachher'
        return m + n
    return adder


f_5 = add_n(5)

print 'vorher'

print f_5(2)
Ergebnis:

Code: Alles auswählen

vorher
nachher
7
Gruß,
Mick.
In specifications, Murphy's Law supersedes Ohm's.
myxin
User
Beiträge: 47
Registriert: Dienstag 10. Januar 2012, 16:57

@pillmuncher
ahhh - heisst, f_5 ist jetzt eine Funktion vom Typ addr, oder?

Und damit wird sie später dann auch ausserhalb der sie umgebenden Funktion ausführbar, richtig?

Danke
Claudia
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

myxin hat geschrieben:ahhh - heisst, f_5 ist jetzt eine Funktion vom Typ addr, oder?
Jein. f_5 ist eine Funktion. Eine Funktion vom Typ "Funktion", weil in Python haben Funktionen nun mal so einen Typ. Es ist an sich die Funktion ``adder`` die den Namen ``m`` als Parameter hat und den Namen ``n`` aus dem Namensraum der Funktion nimmt in der sie definiert wurde (``add_n``).

Da mag auf den ersten Blick ein relativ sinnloses Feature zu sein, aber es gibt einen Programmierstil wo sowas häufig zur Anwendung kommt. Mal ein Beispiel, du willst eine Liste sortieren:

Code: Alles auswählen

collection = [(2, 3), (4, 2), (4, 1), (5, 5)]

sorted(collection)
# [(2, 3), (4, 1), (4, 2), (5, 5)]
Nun willst du aber nicht nach dem ersten Element der Liste sortieren sondern nach dem Zeiten. Die eingebaute Funktion ``sorted`` nimmt einen Parameter namens Key entgegen, der eine Funktion entgegennimmt. Diese Funktion wird auf jedes Element angewendet um die Sortierreihenfolge anzugeben. In unserem Fall wäre das das einfach das zweite Element des Tupels. Also bauen wir so eine Funktion:

Code: Alles auswählen

def second(item):
    return item[1]

sorted(collection, key=second)
# [(4, 1), (4, 2), (2, 3), (5, 5)]
Gut, das ist jetzt aber doof, wenn wir das ändern wollen und etwa nach einem dritten Element sortieren wollen müssten wir ja eine Funktion schreiben, die ``third`` heißt. Und für das vierte Element noch eine, etc. etc. Wie wärs wenn wir statt die Funktion immer hinzuschreiben, sie quasi programmatisch erzeugen? Da kommen verschachtelte Funktionen ins Spiel:

Code: Alles auswählen

def get_nth_item(num):
    def inner(item):
        return item[num]
    return inner
So, jetzt haben wir quasi eine Funktionsfabrik, die uns Funktionen ausgibt, die wir an den ``key``-Parameter von ``sorted`` geben können. Probieren wir das mal aus:

Code: Alles auswählen

sorted(collection, key=get_nth_item(1))
# [(4, 1), (4, 2), (2, 3), (5, 5)]
Oha, das funktioniert ja fein! Damit haben wir unseren Code mittels geschachtelter Funktionen erfolgreich vereinfacht. Prima! Das wäre dann das Ende des Beispiels.

Die nächste Vereinfachung wäre übrigens festzustellen dass es so eine Funktion in der Stdlib bereits gibt:

Code: Alles auswählen

from operator import itemgetter
sorted(collection, key=itemgetter(1))
# [(4, 1), (4, 2), (2, 3), (5, 5)]
Du siehst, ``itemgetter(1)`` gibt dir auch eine verschachtelte Funktion zurück.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
myxin
User
Beiträge: 47
Registriert: Dienstag 10. Januar 2012, 16:57

@Leonidas
vielen Dank für die Erklärung und das Beispiel.
Ich will nicht behaupten, dass ich das mit den nested functions jetzt 100%ig verstanden habe,
aber ich denke ich habe eine gute Idee davon bekommen.

Ich denke ich muss da ein bisschen mit spielen um dies zu vertiefen.

Danke
Claudia
Antworten