2009-08-05 11 views
5

Biorąc pod uwagę funkcjęIntrospekcja zagnieżdżonych (lokalne) funkcji pełnionej funkcji w Pythonie

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

jest jakiś sposób dla mnie, aby uzyskać dostęp do jego lokalnego get() i słupek() funkcji w taki sposób, że można je nazwać ? Szukam funkcji, która będzie działać tak jak z funkcji f() zdefiniowanej powyżej:

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

mogę uzyskać dostęp do kodu obiektów dla tych lokalnych funkcji jak tak

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

co skutkuje

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

, ale nie mogę dowiedzieć się, jak uzyskać rzeczywiste obiekty funkcji wywołania. Czy to w ogóle jest możliwe?

Dzięki za pomoc,

Will.

+0

Należy zauważyć, że na moim konkretnym przypadku zastosowania, muszę być w stanie wywołać lokalne/zagnieżdżone funkcje tak: Get (żądania, * args, ** kwargs) –

Odpowiedz

4

Jesteś bardzo blisko robi, że - po prostu brakuje modułu new:

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 :]. 

Ale dlaczego do cholery przeszkadza? Czy nie jest łatwiej używać klasy? Tworzenie zapytania wygląda jak wywołanie funkcji.

+0

To działa, o ile funkcje zagnieżdżone nie są zamknięciami. Dzięki! Ty i Ants Aasma mają rację, że powinienem po prostu użyć do tego klasy. Powodem, dla którego nie jestem, jest to, że jest to projekt Django, który ma już wiele dekoratorów do niestandardowego uwierzytelniania itp., I nie chciałem dostosować wszystkich tych dekoratorów do pracy z metodami. Więc jestem w pewnym sensie na pół symulujący podejście oparte na klasach do poglądów RESTful (takich jak http://github.com/jpwatts/django-restviews/tree/master). –

+0

Wierzę, że to działa tylko dlatego, że masz f w bieżącym module. – Quant

2

Możesz powrócić funkcje jak każdy inny obiekt w Pythonie:

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


get, post = f() 

nadzieję, że to pomaga!

Pamiętaj jednak, że jeśli chcesz używać zmiennych "x" i "y" w get() lub post(), powinieneś utworzyć listę.

Jeśli zrobisz coś takiego:

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 i post1 będzie odwoływać się do innego 'x' listy niż get2 i post2.

+0

Tak, to rzeczywiście to, co ja w tej chwili. Miałem nadzieję znaleźć sposób na uniknięcie zwracania funkcji jawnie, jeśli to możliwe. –

+3

Will, cytując ** Zen z Python **: * "Jawny jest lepszy niż niejawny." * - i-- * "Specjalne przypadki nie są wystarczająco szczególne, aby złamać zasady." * –

+0

Dobra uwaga, Evan . Rozwiązanie, które mam teraz, które jest w zasadzie takie samo jak to, z wyjątkiem tego, że wymaga odesłania dyktatu zamiast krotki, działa dobrze. Miałem nadzieję, że uniknę powtórki z powodu wprowadzenia dicta powrotu (get = get, post = post, put = put, etc) na dole garści widoków Django. Niemniej dziękuję za poradę. –

2

Można użyć exec, aby uruchomić obiekty kodu. Na przykład, jeśli miał f zdefiniowane jak wyżej, a następnie

exec(f.func_code.co_consts[3]) 

dałoby

get 

jako wyjścia.

+0

Ah, więc w ten sposób wykonujesz obiekt kodu! Niestety, dla mojego rzeczywistego przypadku użycia, muszę móc przekazywać argumenty do lokalnych funkcji. Czy istnieje sposób, aby to zrobić, wywołując exec()? –

1

Wewnętrzne obiekty funkcji nie istnieją przed wykonaniem funkcji f(). Jeśli chcesz je zdobyć, musisz je sam zbudować. Jest to zdecydowanie nietrywialne, ponieważ mogą one być zamknięciami, które przechwytują zmienne z zakresu funkcji i będą wymagały grzebania w obiektach, które prawdopodobnie powinny być traktowane jako szczegóły implementacji interpretera.

Jeśli chcesz zbierać funkcje z mniej powtórzeń Polecam jeden z następujących sposobów:

a) Wystarczy umieścić funkcje w definicji klasy i powrócić odniesienie do tej klasy. Zbiór powiązanych funkcji, które są dostępne po nazwie, pachnie okropnie jak klasa.

b) Utwórz podklasę dict, która ma metodę rejestrowania funkcji i użyj jej jako dekoratora.

Kod tego będzie wyglądać mniej więcej tak:

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) grzebać w miejscowych() i odfiltrować funkcję z inspect.isfunction. (Zwykle nie jest dobrym pomysłem)

+0

Odp: "a) Po prostu umieść funkcje w definicji klasy i zwróć odwołanie do tej klasy. Zbiór powiązanych funkcji, które są dostępne po nazwie, pachnie okropnie jak klasa.", Masz zdecydowanie rację. Zobacz mój komentarz na temat odpowiedzi gavoja poniżej, aby wyjaśnić, dlaczego nie używam klasy w tym przypadku. Dziękuję za wyjaśnienia! –

Powiązane problemy