2009-08-05 2 views
5

Angesichts der Funktionintrospecting eine gegebene Funktion der verschachtelten (local) Funktionen in Python

def f(): 
    x, y = 1, 2 
    def get(): 
     print 'get' 
    def post(): 
     print 'post' 

ist es für mich eine Möglichkeit, seine lokale get() und post() Funktionen in einer Weise zuzugreifen, die ich sie anrufen können ? Ich suche nach einer Funktion, die oben wie so mit der Funktion f() definiert arbeiten:

>>> get, post = get_local_functions(f) 
>>> get() 
'get' 

ich Zugriff auf die Code-Objekte für die lokalen Funktionen wie so

import inspect 
for c in f.func_code.co_consts: 
    if inspect.iscode(c): 
     print c.co_name, c 

was dazu führt,

get <code object get at 0x26e78 ...> 
post <code object post at 0x269f8 ...> 

aber ich kann nicht herausfinden, wie Sie die tatsächlichen aufrufbaren Funktionsobjekte erhalten. Ist das überhaupt möglich?

Danke für Ihre Hilfe,

Will.

+0

ich, dass für meinen eigentlichen Anwendungsfall beachten soll, muß ich die lokalen/verschachtelten Funktionen wie so nennen können: get (Anfrage, * args, ** kwargs) –

Antwort

4

Sie sind ziemlich nah dran, das zu tun - nur fehlt new Modul:

import inspect 
import new 

def f(): 
    x, y = 1, 2 
    def get(): 
     print 'get' 
    def post(): 
     print 'post' 

for c in f.func_code.co_consts: 
    if inspect.iscode(c): 
     f = new.function(c, globals()) 
     print f # Here you have your function :]. 

Aber warum zum Teufel der Mühe machen? Ist es nicht einfacher, den Unterricht zu benutzen? Instanziierung sieht auf jeden Fall wie ein Funktionsaufruf aus.

+0

Das funktioniert, solange die verschachtelten Funktionen keine Closures sind. Vielen Dank! Sie und Ameisen Aasma haben jedoch recht, dass ich nur eine Klasse dafür verwenden sollte. Der Grund, warum ich nicht bin, ist, dass es sich um ein Django-Projekt handelt, das bereits viele Ansichtsdekoratoren für die benutzerdefinierte Authentifizierung usw. verwendet hat, und ich wollte nicht alle diese Dekoratoren so anpassen, dass sie mit Methoden arbeiten. Also bin ich halbherzig und simuliere einen klassenbasierten Ansatz für REST-konforme Ansichten (wie http://github.com/jpwatts/django-restviews/tree/master). –

+0

Ich glaube, das funktioniert nur, weil Sie f im aktuellen Modul haben. – Quant

2

Sie können Funktionen geben wie jedes andere Objekt in Python:

def f(): 
    x, y = 1, 2 
    def get(): 
     print 'get' 
    def post(): 
     print 'post' 
    return (get, post) 


get, post = f() 

hoffe, das hilft!

Beachten Sie jedoch, dass wenn Sie Ihre 'x' und 'y' Variablen in get() oder post() verwenden möchten, sollten Sie sie zu einer Liste machen.

Wenn Sie etwas tun, wie folgt aus:

def f(): 
    x = [1] 
    def get(): 
     print 'get', x[0] 
     x[0] -= 1 
    def post(): 
     print 'post', x[0] 
     x[0] += 1 
    return (get, post) 

get1, post1 = f() 
get2, post2 = f() 

get1 und post1 wird eine andere 'x' Liste als Get2 und post2 verweisen.

+0

Ja, das ist eigentlich das, was ich bin im Moment tun. Ich hatte gehofft, einen Weg zu finden, die Funktionen, wenn möglich, nicht explizit zurückgeben zu müssen. –

+3

Will, um das ** Zen von Python ** zu zitieren: * "Explizit ist besser als implizit." * --and-- * "Spezialfälle sind nicht speziell genug, um die Regeln zu brechen." * –

+0

Guter Punkt, Evan . Die Lösung, die ich gerade habe, die im Wesentlichen die gleiche wie diese ist, außer dass es erfordert, ein Diktat anstelle eines Tupels zurückzugeben, funktioniert gut. Ich hatte nur gehofft, die Wiederholung von dict (get = get, post = post, put = put, etc.) zu vermeiden, die am Ende einer Reihe von Django-Ansichten steht. Danke für den Hinweis. –

2

Sie könnten exec verwenden, um die Codeobjekte auszuführen. Zum Beispiel, wenn Sie hatte, wie oben f definiert, dann

exec(f.func_code.co_consts[3]) 

geben würde

get 

als Ausgabe.

+0

Ah, also, wie Sie ein Code-Objekt ausführen! Leider muss ich für meinen eigentlichen Anwendungsfall Argumente in die lokalen Funktionen übertragen können. Gibt es eine Möglichkeit, das zu tun, wenn ich exec() anrufe? –

1

Die inneren Funktionsobjekte existieren nicht, bevor die Funktion f() ausgeführt wird. Wenn Sie sie bekommen wollen, müssen Sie sie selbst konstruieren. Das ist definitiv nicht trivial, weil es sich um Closures handeln könnte, die Variablen aus dem Funktionsumfang der Funktion erfassen und trotzdem in Objekten herumstochern müssen, die wahrscheinlich als Implementierungsdetails des Interpreters angesehen werden sollten.

Wenn Sie die Funktionen mit weniger Wiederholungen sammeln möchte ich empfehlen, eine der folgenden Ansätze:

a) Sie einfach die Funktionen in einer Klassendefinition setzen und einen Verweis auf diese Klasse zurück. Eine Sammlung verwandter Funktionen, auf die durch den Namen zugegriffen wird, riecht schrecklich nach einer Klasse.

b) Erstellen Sie eine dict-Unterklasse, die über eine Methode zum Registrieren von Funktionen verfügt und diese als Dekorator verwendet.

Der Code für diese würde wie folgt aussehen:

class FunctionCollector(dict): 
    def register(self, func): 
     self[func.__name__] = func 

def f(): 
    funcs = FunctionCollector() 
    @funcs.register 
    def get(): 
     return 'get' 
    @funcs.register 
    def put(): 
     return 'put' 
    return funcs 

c) in Einheimischen herumzustochern() und die Funktion mit inspect.isfunction herauszufiltern. (In der Regel keine gute Idee)

+0

Re: "a) Fügen Sie einfach die Funktionen in eine Klassendefinition und geben Sie einen Verweis auf diese Klasse. Eine Sammlung von verwandten Funktionen, die durch den Namen zugegriffen werden riecht schrecklich wie eine Klasse.", Sie haben definitiv Recht. In meinem Kommentar zu gavojas Antwort unten finden Sie eine Erklärung dafür, warum ich in dieser Instanz keine Klasse verwende. Danke für deine Erklärungen! –

Verwandte Themen