So come funziona il confronto delle funzioni in Python 3 (basta confrontare l'indirizzo in memoria) e capisco perché.Sviluppo di un'euristica per testare semplici funzioni anonime Python per l'equivalenza
Capisco anche che il confronto "vero" (le funzioni f
e g
restituiscono lo stesso risultato dato gli stessi argomenti, per qualsiasi argomento?) È praticamente impossibile.
Sto cercando qualcosa in mezzo. Voglio che il confronto di lavorare su casi più semplici di funzioni identiche, e forse alcuni tra quelli meno banali:
lambda x : x == lambda x : x # True
lambda x : 2 * x == lambda y : 2 * y # True
lambda x : 2 * x == lambda x : x * 2 # True or False is fine, but must be stable
lambda x : 2 * x == lambda x : x + x # True or False is fine, but must be stable
nota che io sono interessato a risolvere questo problema per funzioni anonime (lambda
), ma non mi dispiacerebbe se la soluzione funziona anche per le funzioni con nome.
La motivazione è che all'interno del modulo blist
, sarebbe bello verificare che due istanze di sortedset
abbiano la stessa funzione di ordinamento prima di eseguire un unione, ecc. Su di esse.
Le funzioni con nome sono di minore interesse perché posso assumere che siano diverse quando non sono identiche. Dopo tutto, supponiamo che qualcuno abbia creato due ordinamenti con una funzione con nome nell'argomento key
. Se intendono che queste istanze siano "compatibili" ai fini delle operazioni di set, probabilmente utilizzerebbero la stessa funzione, piuttosto che due funzioni separate denominate che eseguono operazioni identiche.
Posso solo pensare a tre approcci. Tutti sembrano difficili, quindi ogni idea è apprezzata.
bytecodes Confrontando potrebbero funzionare ma potrebbe essere fastidioso che è attivazione dipendente (e quindi il codice che ha lavorato su un pitone rompe su un altro).
La comparazione del codice sorgente con token sembra ragionevole e portabile. Certo, è meno potente (dal momento che è più probabile che le stesse funzioni vengano rifiutate).
Un euristico solido preso in prestito da alcuni libri di calcolo simbolici è teoricamente l'approccio migliore. Potrebbe sembrare troppo pesante per il mio scopo, ma in realtà potrebbe essere una buona misura in quanto le funzioni lambda sono in genere minuscole e quindi potrebbe funzionare velocemente.
EDIT
Un esempio più complesso, sulla base del commento di @delnan:
# global variable
fields = ['id', 'name']
def my_function():
global fields
s1 = sortedset(key = lambda x : x[fields[0].lower()])
# some intervening code here
# ...
s2 = sortedset(key = lambda x : x[fields[0].lower()])
Sarebbe mi aspetto che le funzioni chiave per s1
e s2
per valutare come uguali?
Se il codice di intervento contiene una qualsiasi chiamata di funzione, il valore di fields
può essere modificato, determinando diverse funzioni chiave per s1
e s2
. Dal momento che chiaramente non faremo analisi del flusso di controllo per risolvere questo problema, è chiaro che dobbiamo valutare queste due funzioni lambda come diverse, se stiamo cercando di eseguire questa valutazione prima del runtime. (Anche se fields
non fosse globale, avrebbe potuto esserci un altro nome associato ad esso, ecc.) Ciò ridurrebbe drasticamente l'utilità di tutto questo esercizio, poiché poche funzioni lambda non avrebbero alcuna dipendenza dall'ambiente.
EDIT 2:
mi sono reso conto che è molto importante confrontare gli oggetti funzione come esistono in fase di esecuzione. Senza di ciò, tutte le funzioni che dipendono dalle variabili dall'ambito esterno non possono essere confrontate; e le funzioni più utili hanno tali dipendenze. Considerato in runtime, tutte le funzioni con la stessa firma sono comparabili in modo pulito e logico, indipendentemente da cosa dipendono, se sono impure, ecc.
Di conseguenza, ho bisogno non solo del bytecode ma anche del stato globale al momento della creazione dell'oggetto funzione (presumibilmente __globals__
). Quindi devo abbinare tutte le variabili dall'ambito esterno ai valori da __globals__
.
Sei consapevole e hai tenuto conto del fatto che il significato di un oggetto funzione può cambiare drasticamente a seconda di quale ambiente è chiuso? Ad esempio, due oggetti funzione creati da 'lambda: 1/0 se foo else 1' varieranno selvaggiamente nel loro comportamento se uno si chiude su' foo = True' e l'altro si chiude su 'foo = False'. E non pensiamo nemmeno alle funzioni impure e 'eval' ... – delnan
@delnan Grazie, non ci ho pensato! Sto aggiornando la domanda. – max
Per risolvere il problema, non hai bisogno di quello che stai chiedendo. Ti interessa solo se due lambda sono * ordinabili in modo simile *. Come sono i tuoi dati? Potresti semplicemente dar loro un campione, ma non è affidabile al 100%. Direi che la soluzione migliore (per il tuo problema reale) sarebbe un design migliore. –