Davvero non vuoi andare in questa tana del coniglio, ma se insisti, è possibile. Con un po 'di lavoro.
La funzione annidata è creato nuovo per ogni chiamata a make_adder()
:
>>> import dis
>>> dis.dis(make_adder)
2 0 LOAD_CLOSURE 0 (x)
3 BUILD_TUPLE 1
6 LOAD_CONST 1 (<code object adder at 0x10fc988b0, file "<stdin>", line 2>)
9 MAKE_CLOSURE 0
12 STORE_FAST 1 (adder)
4 15 LOAD_FAST 1 (adder)
18 RETURN_VALUE
Il MAKE_CLOSURE
codice operativo non crea una funzione con una chiusura, una funzione annidata riferimento alla x
dalla funzione genitore (il LOAD_CLOSURE
codice operativo costruisce la cella di chiusura per la funzione).
Senza chiamare la funzione make_adder
, è possibile accedere solo all'oggetto codice; è memorizzato come costante con il codice funzione make_adder()
. Il codice byte adder
conta sul fatto di poter accedere alla variabile x
come una cellula ambito, tuttavia, che rende il codice oggetto quasi inutile:
>>> make_adder.__code__.co_consts
(None, <code object adder at 0x10fc988b0, file "<stdin>", line 2>)
>>> dis.dis(make_adder.__code__.co_consts[1])
3 0 LOAD_DEREF 0 (x)
3 LOAD_FAST 0 (y)
6 BINARY_ADD
7 RETURN_VALUE
LOAD_DEREF
carica un valore da una cella di chiusura. Per rendere l'oggetto codice in un nuovo oggetto funzione, che avrebbe dovuto passare che al costruttore funzione di:
>>> from types import FunctionType
>>> FunctionType(make_adder.__code__.co_consts[1], globals(),
... None, None, (5,))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: arg 5 (closure) expected cell, found int
ma come si può vedere, il costruttore si aspetta di trovare una chiusura, non è un valore intero. Per creare una chiusura, abbiamo bisogno, beh, di una funzione che abbia variabili libere; quelli contrassegnati dal compilatore come disponibili per la chiusura. E ha bisogno di restituire quei valori chiusi su di noi, non è possibile creare una chiusura altrimenti. Quindi, creiamo una funzione annidata solo per la creazione di una chiusura:
def make_closure_cell(val):
def nested():
return val
return nested.__closure__[0]
cell = make_closure_cell(5)
Ora possiamo ricreare adder()
senza chiamare make_adder
:
>>> adder = FunctionType(make_adder.__code__.co_consts[1], globals(),
... None, None, (cell,))
>>> adder(10)
15
forse solo chiamando make_adder()
sarebbe stato più semplice.
Per inciso, come potete vedere, le funzioni sono oggetti di prima classe in Python. make_adder
è un oggetto e con l'aggiunta di si invoca o chiamata la funzione. In questo caso, quella funzione restituisce un altro oggetto funzione, uno che è possibile chiamare anche. Nell'esempio tortuoso sopra riportato su come creare adder()
senza chiamare make_adder()
, ho fatto riferimento all'oggetto funzione make_adder
senza chiamarlo; per smontare il codice byte Python ad esso collegato, o per recuperare costanti o chiusure da esso, per esempio.Allo stesso modo, la funzione make_adder()
restituisce l'oggetto funzione adder
; il punto di make_adder()
è quello di creare quella funzione per qualcos'altro per poi chiamarlo.
La sessione precedente è stata condotta tenendo conto della compatibilità tra Python 2 e 3. Le versioni precedenti di Python 2 funzionano allo stesso modo, anche se alcuni dettagli differiscono un po '; alcuni attributi hanno nomi diversi, ad esempio func_code
anziché __code__
, ad esempio. Consulta la documentazione su questi nello inspect
module e nello Python datamodel se vuoi conoscere i dettagli nitty grintosi.
equivalente: 'make_adder = lambda x: lambda y: x + y' ora, puoi chiamare il' lambda 'interno? – Elazar
sì! 'make_adder = make_adder (...)', quindi 'make_adder (...)' – ovrwngtvity
Che è, no, non è possibile. – Elazar