Python: определение новых функций на лету с помощью «с» - PullRequest
4 голосов
/ 04 февраля 2010

Я хочу преобразовать следующий код:

...
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)

Ответы [ 7 ]

6 голосов
/ 04 февраля 2010

Нет смысла использовать with здесь. Вместо этого используйте понимание списка:

funcs = [getFunc(url, links) for url in urls]
4 голосов
/ 04 февраля 2010

Немного нетрадиционно, но вы можете заставить декоратор зарегистрировать функцию и связать любые переменные цикла в качестве аргументов по умолчанию:

urls = [many urls]
links = []
funcs = []

for url in urls:
    @funcs.append
    def func(url=url):
        page = open(url)
        link = searchForLink(page)
        links.append(link)
2 голосов
/ 04 февраля 2010

Существует только два способа создания функций: def и lambda. Лямбды предназначены для крошечных функций, поэтому они могут не подходить для вашего случая Однако, если вы действительно хотите, вы можете заключить две лямбды друг в друга:

urls = [many urls]
links = []
funcs = [(lambda x:
            lambda:
              links.append(searchForLink(open(x))))(u)
         for u in urls]

Слишком LISPish на мой вкус.

2 голосов
/ 04 февраля 2010

Потерять линию <STATEMENT>(funcs):

Edit:

Я имею в виду: зачем ты это делаешь? Зачем определять новую функцию для каждой страницы? Почему бы просто не сделать это?

urls = [many urls]
links = []
for url in urls:
    page = open(url)
    link = searchForLink(page)
    links.append(link) 
1 голос
/ 05 февраля 2010

Возможно, вам лучше использовать генераторы для достижения отсроченных вычислений, которые вы ищете.

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

1 голос
/ 04 февраля 2010

Старый добрый 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__ больше работы, чем просто написать вспомогательную функцию.

1 голос
/ 04 февраля 2010

Вы не должны использовать «с» для этого (хотя, учитывая, что это Python, вы почти наверняка могли бы использовать какой-то странный побочный эффект и динамизм Python).

Цель "with" в Python - это , как описано в документации , "чтобы обернуть выполнение блока методами, определенными менеджером контекста. Это позволяет использовать обычную попытку ... кроме. ..последовательности использования шаблонов для удобного повторного использования. "

Я думаю, что вы путаете "с" Python с Javascript / VisualBasic"с", который может быть косметически похожим, но фактически не связанным.

...