2013-06-17 10 views
6

Mam obiekt lambda, który jest tworzony w tej funkcji:Jak odwzorować wpisy func_closure na nazwy zmiennych?

def add_url_rule(self, rule, endpoint=None, view_func=None, **options): 
    self.record(lambda s: 
     s.add_url_rule(rule, endpoint, view_func, **options)) 

Korzystanie func_closure obiektu funkcji lambda mogę uzyskać dostęp do zakresu zamknięcia funkcji lambda:

(<cell at 0x3eb89f0: str object at 0x2fb4378>, 
<cell at 0x3eb8a28: function object at 0x3cb3a28>, 
<cell at 0x3eb8a60: str object at 0x3ebd090>, 
<cell at 0x3eb8b08: dict object at 0x3016ec0>) 

się bliżej (w atrybucie cell_contents każdego obiektu cell) pokazuje mi tak:

>>> [c.cell_contents for c in func.func_closure] 
['categoryDisplay', 
<function indico.web.flask.util.RHCategoryDisplay>, 
'/<categId>/', 
{}] 

że La Funkcja MBDA został stworzony przez tego zaproszenia:

add_url_rule('/<categId>/', 'categoryDisplay', rh_as_view(RHCategoryDisplay)) 

Jak widać, kolejność nie odpowiada kolejności argument funkcji lub kolejności, w której argumenty są używane wewnątrz lambda. Chociaż mogłem łatwo stwierdzić, który element jest oparty na jego typie/treści, chciałbym to zrobić w bardziej przejrzysty sposób.

Moje pytanie brzmi: jak mogę powiązać je z ich oryginalnymi nazwami zmiennych (lub co najmniej pozycjami w przypadku argumentów funkcji)?

+0

Wygląda argumentów kluczowych podejmowane są pierwsze, następnie pozycje, a następnie catch-all. –

+0

Czy jest to testowane w Pythonie 2 lub 3? –

+1

Używam Pythona 2.7, ale musi on obsługiwać wersje 2.6 i 2.7. Nie sądzę jednak, aby istniały jakiekolwiek istotne zmiany między tymi dwiema wersjami. – ThiefMaster

Odpowiedz

12

Zamknięcia są tworzone przez LOAD_CLOSURE kodu bajtowego, w tej samej kolejności, jak ich bytecodes są sortowane:

>>> dis.dis(add_url_rule) 
    2   0 LOAD_FAST    0 (self) 
       3 LOAD_ATTR    0 (record) 
       6 LOAD_CLOSURE    0 (endpoint) 
       9 LOAD_CLOSURE    1 (options) 
      12 LOAD_CLOSURE    2 (rule) 
      15 LOAD_CLOSURE    3 (view_func) 
      18 BUILD_TUPLE    4 
      21 LOAD_CONST    1 (<code object <lambda> at 0x10faec530, file "<stdin>", line 2>) 
      24 MAKE_CLOSURE    0 
      27 CALL_FUNCTION   1 
      30 POP_TOP    
      31 LOAD_CONST    0 (None) 
      34 RETURN_VALUE   

, więc kolejność jest ustalana podczas kompilacji, przez compiler_make_closure(); ta funkcja używa krotki func.func_code.co_freevars jako przewodnika, który wymienia zamknięcia w tej samej kolejności.

func.func_code.co_freevars jest podczas tworzenia się obiekt kodu w makecode i krotka jest generowany z klawiszy Pythonowego słownika, tak więc kolejność jest dowolna inaczej, jako wspólne dla słowników. Jeśli jesteś ciekawy, to dict jest zbudowany w compiler_enter_scope(), używając dictbytype() utility function ze wszystkich wolnych zmiennych wymienionych w tabeli symboli kompilatora, sam słownik python.

Więc kolejność zamknięć jest rzeczywiście arbitralny (tabela hash zamówiony), a chcesz używać func.func_code.co_freevars krotki dołączyć nazwy zamknięć:

dict(zip(func.func_code.co_freevars, (c.cell_contents for c in func.func_closure))) 
5

Dzięki YHg1s w #python na Freenode odkryłem: func_code.co_freevars to krotka zawierająca nazwy zmiennych elementów w zamknięciu.

>>> func.func_code.co_freevars 
('endpoint', 'view_func', 'rule', 'options') 

Więc tworząc nazwy mapowania dict do wartości zamknięcia jest łatwe:

>>> dict(zip(func.func_code.co_freevars, 
      (c.cell_contents for c in func.func_closure))) 
{'endpoint': 'categoryDisplay', 
'options': {}, 
'rule': '/<categId>/', 
'view_func': <function indico.web.flask.util.RHCategoryDisplay>} 
+0

Ah, i tutaj zamierzałem odpowiedzieć * dlaczego * są w takiej kolejności, w jakiej są. –

Powiązane problemy