Python: definizione di nuove funzioni al volo usando “con”
-
18-09-2019 - |
Domanda
voglio convertire il seguente codice:
...
urls = [many urls]
links = []
funcs = []
for url in urls:
func = getFunc(url, links)
funcs.append(func)
...
def getFunc(url, links):
def func():
page = open(url)
link = searchForLink(page)
links.append(link)
return func
nel codice molto più conveniente:
urls = [many urls]
links = []
funcs = []
for url in urls:
<STATEMENT>(funcs):
page = open(url)
link = searchForLink(page)
links.append(link)
speravo di fare questo con l'affermazione with
. Come ho commentato a soffietto, speravo di ottenere:
def __enter__():
def func():
..code in the for loop..
def __exit__():
funcs.append(func)
Naturalmente questo non funziona.
di lista non è un bene per i casi sono stati i searchForLink
azione non è solo una funzione, ma molte funzioni. Si sarebbe trasformato in un codice estremamente illeggibile. Per esempio anche questo sarebbe problematico con list comprehension:
for url in urls:
page = open(url)
link1 = searchForLink(page)
link2 = searchForLink(page)
actionOnLink(link1)
actionOnLink(link2)
.... many more of these actions...
links.append(link1)
Soluzione
Un po 'non convenzionale, ma si può avere un decoratore registrare il func e legare tutte le variabili di loop come argomenti di default:
urls = [many urls]
links = []
funcs = []
for url in urls:
@funcs.append
def func(url=url):
page = open(url)
link = searchForLink(page)
links.append(link)
Altri suggerimenti
Non ha senso usare with
qui. Invece di utilizzare una lista di comprensione:
funcs = [getFunc(url, links) for url in urls]
Ci sono solo due modi per creare funzioni: def
e lambda
. Lambda sono pensati per le funzioni di piccole, in modo che non possano essere molto appropriato per il vostro caso. Tuttavia, se si vuole veramente, è possibile racchiudere due lambda uno dentro l'altro:
urls = [many urls]
links = []
funcs = [(lambda x:
lambda:
links.append(searchForLink(open(x))))(u)
for u in urls]
Un po 'troppo per i miei gusti LISPish.
Perdere la <STATEMENT>(funcs):
riga
Modifica:
Voglio dire: perché si dovrebbe fare questo? Perché definire una nuova funzione per ogni pagina? Perché non farlo?
urls = [many urls]
links = []
for url in urls:
page = open(url)
link = searchForLink(page)
links.append(link)
Non si deve usare "con" per fare questo (anche se, dato che si tratta di Python, è quasi certamente possibile, utilizzando un bizzarro effetto collaterale e dynamicism di Python).
Lo scopo di "con" in Python è, come descritto in la documentazione , "per avvolgere l'esecuzione di un blocco con metodi definiti da un contesto gestore. Ciò consente try comune ... tranne ... infine schemi di utilizzo da incapsulare per un comodo riutilizzo."
Credo che tu stai confondendo "con" con la JavaScript / VisualBasic "con", che può essere esteticamente simili, ma che è effettivamente correlati.
Il buon vecchio itertools .
from itertools import imap
links.extend(imap(searchForLink, imap(open, urls)))
Anche se, forse si preferisce funzionale .
from functional import *
funcs = [partial(compose(compose(links.append, searchForLink), open), url) for url in urls]
for func in funcs: func()
Non credo che sia utile creare una classe per uso with
: è più lavoro per creare __enter__
e __exit__
di quanto non lo è quello di scrivere solo una funzione di supporto
Si può essere meglio utilizzare i generatori per ottenere il calcolo ritardata che stai cercando.
def MakeLinks(urls):
for url in urls:
page = open(url)
link = searchForLink(page)
yield link
links = MakeLinks(urls)
Quando si desidera che il link:
for link in links:
print link
Gli URL sarà cercato nel corso di questo ciclo, e non tutti in una volta (che sembra che si sta Tring per evitare).