Питон:определение новых функций на лету с использованием «with»

StackOverflow https://stackoverflow.com/questions/2200026

  •  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-адреса будут просматриваться во время этого цикла, а не все сразу (чего, похоже, вы пытаетесь избежать).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top