2017-01-30 2 views
1

In JavaScript könnte ich eine Funktion wie folgt schreiben:Gibt es eine "pythonische" Art, diese Schließung zu schreiben?

function counter() { 
    foo = 0; 
    function increment() { 
    foo += 1 
    console.log(foo); 
    } 
    function printVal() { 
    console.log(foo); 
    } 

    return { 
       increment: increment, 
      printVal: printVal, 
        }  
} 

func = counter(); 

func.increment() 
func.increment() 
func.printVal() 

ich eine ähnliche Funktion mit Python zu erreichen zu versuchen. Vor allem, wie ich über die Rückgabe der äußeren Funktion auf beide inneren Funktionen zugreifen kann. Hier

ist ein funktionierendes, aber lustig suchen, Version in Python:

def counter(): 
    foo = {'bar': 0} 
    def increment(): 
     foo['bar'] += 1 
     return foo['bar'] 
    def printVal(): 
     return foo['bar'] 
    return {'increment': increment, 'printVal': printVal} 

func = counter() 
func['increment']() 
func['printVal']() 

Gibt es irgendeine Art von eleganter oder ‚pythonic‘ Art und Weise einen Verschluss wie diese zu schreiben?

+2

In diesem Fall möchten Sie wahrscheinlich das Objekt 'collections.Counter' verwenden oder einfach eine Klasse schreiben. – monkut

+1

Sie sollten sich fragen, warum Sie in JS überhaupt eine Schließung verwenden: Schließungen sind oft ein Ersatz für _classlike_ Verhalten. Im Fall von Python könnte man tatsächlich eine 'Klasse' verwenden, um das Accoring-Verhalten zu modellieren. Und Ihre beiden Funktionen werden zu realen Methoden. –

Antwort

1

einfach implementieren __getitem__

class Foo: 
    class_attributes = {'a': 3, 
         'b': 5} 

    def __getitem__(self, name): 
     return Foo.class_attributes[name] 


f = Foo() 
print f['a'] 

Ausgang:

3 
+0

Würden die gleichen class_attributes für alle Instanzen von Foo hier verwendet werden? – fafl

+0

In diesem Beispiel wird es. Es ist möglich, pro Instanz class_attributes durch Implementierung des Konstruktors zu definieren. – AndreyF

0

Ich denke, dass Sie so etwas wie das folgende Beispiel erreichen, was Sie wollen, ist:

def makeInc(x): 
    def inc(y): 
    # x is "attached" in the definition of inc 
    return y + x 

    return inc 

incOne = makeInc(1) 
incFive = makeInc(5) 

incOne (5) # returns 6 
incFive(5) # returns 10 

Sie wollen die pythonic Weg Schließungen schaffen. Das obige Beispiel zeigt also, wie man das macht.

3

Python ist nicht so stark wie andere Sprachen in Closures. Erstens unterstützt es nur das Lesen der "geschlossenen" Variablen und zweitens macht es die return-Anweisung etwas unangenehmer.

Auf der anderen Seite ist es mit Klassen sehr prägnant ist, also, wenn Sie ein Datenelement mit zwei Funktionen wollen, würde ich es mit einer Klasse tun:

class Counter: 
    def __init__(self, c=0): 
     self.count = c 
    def increment(self): 
     self.count += 1 
    def printVal(self): 
     return self.count 


c = Counter() 
c.increment() 
print(c.printVal()) 

ich wage zu sagen, dass in diesem Fall, das wäre der pythonische Weg.

EDIT

Nach Ihre Kommentare über Tracking-Funktion Anrufe zu sehen, füge ich diese. Sie können es durch eine Schließung, wie folgt machen:

# this is the tracked function 
def add2(a, b): 
    return a + b 

# this is the count tracker 
def counterize(func): 
    c = [0] 
    def counter_func(*args, **kw): 
     c[0] += 1 
     counter_func.count = c[0] 
     return func(*args, **kw) 
    return counter_func 


cadd2 = counterize(add2) 

print(cadd2(1, 2)) 
print(cadd2(3, 4)) 
print('Called %s times' % cadd2.count) 

>>> 
3 
7 
Called 2 times 

Aber das ist nicht idiomatisch in Python. Auch count im Funktionsobjekt zu behalten ist ein netter Trick, aber das ist es. Ein Trick. In LISP oder Scala dagegen wäre eine Schließung natürlicher, aber dann glaube ich nicht, dass es möglich wäre, count als ein Feld zu behalten, sondern es zusammen mit dem Ergebnis zurückzugeben.

Ich würde sagen, dass in diesem Fall wieder der idiomatische Python-Code über Klassen sein würde, ist es verständlich IMO und ist von der gleichen Länge des Codes:

class Counterize: 
    def __init__(self, func): 
     self.func = func 
     self.count = 0 
    def __call__(self, *args, **kwargs): 
     self.count += 1 
     return self.func(*args, **kwargs) 


cadd2 = Counterize(add2) 
print(cadd2(1, 2)) 
print(cadd2(3, 4)) 
print('Called %s times' % cadd2.count) 

Und der Ausgang wäre die gleich. Der Zweck von __call__ ist es, das Objekt als eine Funktion durch Aufruf mit Klammern behandeln zu können.

+0

Danke, ich habe gehofft, eine Schließung zu verwenden, weil ich versuche, mehr funktionale Techniken zu lernen, aber das sieht nach der besten Lösung aus. Das größere Bild von dem, was ich tun möchte, ist ein Zähler, mit dem ich verfolgen kann, wie oft eine Funktion aufgerufen wurde. –

Verwandte Themen