Питон:определение новых функций на лету с использованием «with»
-
18-09-2019 - |
Вопрос
Я хочу преобразовать следующий код:
...
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
в гораздо более удобный код:
urls = [many urls]
links = []
funcs = []
for url in urls:
<STATEMENT>(funcs):
page = open(url)
link = searchForLink(page)
links.append(link)
Я надеялся сделать это с помощью with
заявление.Как я прокомментировал ниже, я надеялся достичь:
def __enter__():
def func():
..code in the for loop..
def __exit__():
funcs.append(func)
Конечно, это не работает.
Понимание списков не подходит для случаев, когда действие searchForLink
это не одна функция, а множество функций.Это превратилось бы в крайне нечитаемый код.Например, даже это будет проблематично при понимании списков:
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)
Решение
Немного необычно, но вы можете поручить декоратору зарегистрировать функцию и привязать любые переменные цикла в качестве аргументов по умолчанию:
urls = [many urls]
links = []
funcs = []
for url in urls:
@funcs.append
def func(url=url):
page = open(url)
link = searchForLink(page)
links.append(link)
Другие советы
Нет смысла использовать with
здесь.Вместо этого используйте понимание списка:
funcs = [getFunc(url, links) for url in urls]
Есть только два способа создания функций: def
и lambda
.Лямбды предназначены для крошечных функций, поэтому они могут не подойти для вашего случая.Однако, если очень хочется, можно вложить две лямбды друг в друга:
urls = [many urls]
links = []
funcs = [(lambda x:
lambda:
links.append(searchForLink(open(x))))(u)
for u in urls]
На мой вкус, слишком LISPish.
Потерять линию <STATEMENT>(funcs):
Редактировать:
Я имею в виду:зачем тебе это делать?Зачем определять новую функцию для каждой страницы?Почему бы просто не сделать это?
urls = [many urls]
links = []
for url in urls:
page = open(url)
link = searchForLink(page)
links.append(link)
Для этого не следует использовать «with» (хотя, учитывая, что это Python, вы почти наверняка могли бы это сделать, используя какой-то причудливый побочный эффект и динамику Python).
Цель «с» в Python: как описано в документах, "чтобы обернуть выполнение блока методами, определенными менеджером контекста.Это позволяет инкапсулировать общие шаблоны использования try...кроме...finally для удобного повторного использования".
Я думаю, вы путаете «с» Python с Javascript/Visual Basic «с», которое может быть внешне похоже, но фактически не связано.
Старый добрый itertools.
from itertools import imap
links.extend(imap(searchForLink, imap(open, urls)))
Хотя, возможно, вы бы предпочли функциональный.
from functional import *
funcs = [partial(compose(compose(links.append, searchForLink), open), url) for url in urls]
for func in funcs: func()
Я не думаю, что стоит создавать класс для with
использовать:больше работы над созданием __enter__
и __exit__
чем просто написать вспомогательную функцию.
Возможно, вам лучше использовать генераторы для достижения отложенных вычислений, которые вам нужны.
def MakeLinks(urls):
for url in urls:
page = open(url)
link = searchForLink(page)
yield link
links = MakeLinks(urls)
Если вам нужны ссылки:
for link in links:
print link
URL-адреса будут просматриваться во время этого цикла, а не все сразу (чего, похоже, вы пытаетесь избежать).