Python: la definición de nuevas funciones sobre la marcha utilizando “con”
-
18-09-2019 - |
Pregunta
Quiero convertir el código siguiente:
...
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
en el código mucho más conveniente:
urls = [many urls]
links = []
funcs = []
for url in urls:
<STATEMENT>(funcs):
page = open(url)
link = searchForLink(page)
links.append(link)
Tenía la esperanza de hacer esto con la declaración with
. Como os comentaba abajo, esperaba lograr:
def __enter__():
def func():
..code in the for loop..
def __exit__():
funcs.append(func)
Por supuesto, esto no funciona.
Las listas por comprensión no es bueno para los casos fueron los searchForLink
la acción no es sólo una función, pero muchas funciones. Se convertiría en un código muy ilegible. Por ejemplo, incluso esto podría ser problemático con las listas por comprensión:
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)
Solución
Un poco convencional, pero puede tener un decorador de registrar el func y enlazar cualquier variable de bucle como argumentos por defecto:
urls = [many urls]
links = []
funcs = []
for url in urls:
@funcs.append
def func(url=url):
page = open(url)
link = searchForLink(page)
links.append(link)
Otros consejos
No tiene sentido utilizar with
aquí. En lugar de utilizar una lista de comprensión:
funcs = [getFunc(url, links) for url in urls]
Sólo hay dos maneras de crear funciones: def
y lambda
. Lambdas son para pequeñas funciones, por lo que pueden no ser muy apropiado para su caso. Sin embargo, si realmente desea, puede incluir dos lambdas uno dentro de otro:
urls = [many urls]
links = []
funcs = [(lambda x:
lambda:
links.append(searchForLink(open(x))))(u)
for u in urls]
Un poco demasiado LISPish para mi gusto.
Pierde la <STATEMENT>(funcs):
línea
Editar:
Es decir: ¿por qué haces esto? ¿Por qué definir una nueva función para cada página? ¿Por qué no acaba de hacer esto?
urls = [many urls]
links = []
for url in urls:
page = open(url)
link = searchForLink(page)
links.append(link)
No use "con" para hacer esto (a pesar de que, dado que es Python, es casi seguro que podría, utilizando algún extraño efecto secundario y dynamicism de Python).
El propósito de "con" en Python es, como se describe en los documentos , "para envolver la ejecución de un bloque con los métodos definidos por un gestor de contexto. Esto permite que intento común ... excepto ... finalmente patrones de uso para ser encapsulado para su reutilización conveniente."
Creo que estás confundiendo "con" con el Javascript / VisualBasic "con", que puede ser cosméticamente similar pero que es efectivamente no relacionado.
from itertools import imap
links.extend(imap(searchForLink, imap(open, urls)))
A pesar de que, tal vez usted prefiere funcional.
from functional import *
funcs = [partial(compose(compose(links.append, searchForLink), open), url) for url in urls]
for func in funcs: func()
No creo que vale la pena crear una clase para uso with
: es más trabajo para crear __enter__
y __exit__
de lo que es simplemente escribir una función de ayuda
Usted puede ser mejor usar generadores de lograr el cómputo retraso que está buscando.
def MakeLinks(urls):
for url in urls:
page = open(url)
link = searchForLink(page)
yield link
links = MakeLinks(urls)
Cuando se desea que el enlaces:
for link in links:
print link
Las direcciones URL se pueden consultar en este bucle, y no todos a la vez (que parece que estés Tring para evitar).