2012-08-08 3 views
11

Gibt es pythonic Möglichkeiten, den Status zu erhalten (zum Beispiel für Optimierungszwecke), ohne vollständig objektorientiert zu werden?Wie man den Zustand in Python ohne Klassen erhält?

Auf meine Frage besser, hier ist ein Beispiel für ein Muster, das ich häufig verwenden in JavaScript illustrieren:

var someFunc = (function() { 
    var foo = some_expensive_initialization_operation(); 
    return someFunc (bar) { 
     // do something with foo and bar 
    } 
}()); 

Äußerlich ist dies nur eine Funktion wie jede andere, ohne dass Gegenstände oder so etwas zu initialisieren, aber die Schließung erlaubt es, Werte einzeln zu berechnen, die ich dann im Wesentlichen als Konstanten benutze.

Ein Beispiel hierfür in Python ist beim Optimieren regulärer Ausdrücke - es ist nützlich zu verwenden re.compile und gespeichert die kompilierte Version für match und search Operationen.

Die einzigen Möglichkeiten, die ich kenne, dies in Python zu tun, indem Sie eine Variable im Modulumfang einstellen:

compiled_regex = compile_my_regex() 

def try_match(m): # In reality I wouldn't wrap it as pointlessly as this 
    return compiled_regex.match(m) 

Oder durch Erstellen einer Klasse:

class MatcherContainer(object): 
    def __init__(self): 
     self.compiled_regex = compile_my_regex() 
    def try_match(self, m): 
     self.compiled_regex.match(m) 

my_matcher = MatcherContainer() 

Der frühere Ansatz ad -hoc und es ist nicht sehr klar, dass die Funktion und die oben deklarierte Variable miteinander verknüpft sind. Es sitzt auch verschmutzt den Namensraum des Moduls ein bisschen, mit dem ich nicht allzu glücklich bin.

Der letztere Ansatz scheint ausführlich und ein wenig schwer auf dem Boilerplate.

Der einzige andere Weg, den ich mir vorstellen kann, ist, alle Funktionen wie diese in separaten Dateien (Module) zu berücksichtigen und einfach die Funktionen zu importieren, so dass alles sauber ist.

Irgendwelche Ratschläge von erfahreneren Pythonern, wie man damit umgeht? Oder machen Sie sich einfach keine Sorgen und machen Sie weiter mit dem Problem?

+1

Ich persönlich sehe kein Problem mit Ihrer Klassenerstellung. Ausführlichkeit ist gut. "Explizit ist besser als implizit." – tMC

+0

Eine Möglichkeit, um zumindest die Verwendung dieser Klasse weniger ausführlich zu machen, wäre, "try_match" in "__call__" umzubenennen, wodurch Sie es (nach der Konstruktion) genau wie eine Funktion verwenden können. Aber wie in der Antwort von @glglgl, übersetzt sich Ihr JavaScript Code direkt in Python. – lvc

+0

ist dieser Aufruf von someFunc in Ihrem ersten Beispiel korrekt? Oder sollte es Funktionsdefinition sein? – soulcheck

Antwort

10

Sie können auch mit Standardargumente erreichen dies:

def try_match(m, re_match=re.compile(r'sldkjlsdjf').match): 
    return re_match(m) 

da Standardargumente werden nur einmal ausgewertet, bei Modul Import Zeit.

Oder noch einfacher:

try_match = lambda m, re_match=re.compile(r'sldkjlsdjf').match: re_match(m) 

Oder einfachste, aber:

try_match = re.compile(r'sldkjlsdjf').match 

Das spart nicht nur die Wieder Zeit kompiliert (die eigentlich intern ohnehin in dem Re-Modul zwischengespeichert wird), sondern auch die Nachschlagen der '.match' Methode. In einer beschäftigten Funktion oder einer engen Schleife, diese "." Auflösungen können sich addieren.

+0

Danke für die Antwort. Ich weiß über das Modul-Caching Bescheid, aber selbst in einem Skript, bei dem ich einen einzelnen regulären Ausdruck in einigen engen Schleifen verwende, habe ich eine deutliche Leistungssteigerung durch Verwendung von 're.compile' erreicht, anstatt mich auf das eingebaute Caching zu verlassen. – Cera

5

Was

def create_matcher(re): 
    compiled_regex = compile_my_regex() 
    def try_match(m): 
     return compiled_regex.match(m) 
    return try_match 

matcher = create_matcher(r'(.*)-(.*)') 
print matcher("1-2") 

?

Aber Klassen sind in den meisten Fällen besser und sauberer.

+1

+1 für "Aber Klassen sind in den meisten Fällen besser und sauberer." – tMC

12

Sie können den Abschluss in Python auf die gleiche Weise definieren, wie Sie einen Abschluss in JavaScript definieren.

def get_matcher(): 
    compiled_regex = compile_my_regex() 

    def try_match(m) 
     return compiled_regex.match(m) 

    return try_match 

jedoch in Python 2.x Schließungen sind schreibgeschützt (Sie können nicht zu compiled_regex innen Funktionsaufruf neu zuweisen, für das Beispiel oben). Wenn die Closure-Variable eine veränderbare Datenstruktur ist (z. B. list, dict, set), können Sie sie jedoch innerhalb Ihres Funktionsaufrufs ändern.

def get_matcher(): 
    compiled_regex = compile_my_regex() 
    match_cache = {} 

    def try_match(m): 
     if m not in match_cache: 
      match_cache[m] = compiled_regex.match(m) 

     return match_cache[m] 

    return try_match 

In Python 3.x können Sie das nonlocal Schlüsselwort verwenden, um Verschluss Variable in Funktionsaufruf neu zuweisen. (PEP-3104)

Auch die folgenden Fragen zur Schließung in Python sehen:

+1

Nur hinzufügen: Python 3 führt 'nonlocal' ein, das verwendet werden kann, um expliziten Schreibzugriff auf geschlossene Variablen zu geben. [(PEP 3104)] (http://www.python.org/dev/peps/pep-3104/) – rwos

+1

habe die Antwort aktualisiert – Imran

+0

Ich bin ein wenig verwirrt von diesem. Warum sollte nicht der gesamte Körper von 'get_matcher() 'jedes Mal ausgewertet werden? Oder ist der Zweck, dann etwas wie "try_match = get_matcher()" zu tun? – Cera

1

Eine häufig verwendete Konvention ist privat auf Modulebene Globals mit einem Unterstrich vorangestellt, um anzuzeigen, Sie sind nicht Teil der exportierten API des Moduls:

# mymodule.py 

_MATCHER = compile_my_regex() 

def try_match(m): 
    return _MATCHER.match(m) 

Sie sollten nicht davon abgehalten werden, dies zu tun - es ist vorzuziehen, eine versteckte Variable in einer Funktion Schließung.

2

Sie können ein Attribut in einer beliebigen Funktion speichern. Da der Funktionsname global ist, können Sie ihn in anderen Funktionen abrufen. Zum Beispiel:

def memorize(t): 
    memorize.value = t 

def get(): 
    return memorize.value 

memorize(5) 
print get() 

Ausgang:

5 

Sie können es verwenden Zustand in einer einzigen Funktion zu speichern:

def memory(t = None): 
    if t: 
     memory.value = t 
    return memory.value 

print memory(5) 
print memory() 
print memory() 
print memory(7) 
print memory() 
print memory() 

Ausgang:

5 
5 
5 
7 
7 
7 

Zugegeben seine Nützlichkeit ist begrenzt. Ich habe es nur auf SO in this question verwendet.