【问题标题】:Python: defining new functions on the fly using "with"Python:使用“with”动态定义新函数
【发布时间】:2010-02-04 13:41:00
【问题描述】:

我要转换以下代码:

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

【问题讨论】:

  • 首先:为什么要传递函数?是的,有 个用例,但它们并不常见。其次,你为什么使用函数?功能没有错。 :)
  • 如果你迷路了(就像我一样),这个问题的答案是:使用列表理解。

标签: python with-statement


【解决方案1】:

在这里使用with 是没有意义的。而是使用列表推导:

funcs = [getFunc(url, links) for url in urls]

【讨论】:

  • 这会调用函数,他要返回函数对象。
  • 我不想使用函数getFunc()。这就是重点
  • 我喜欢人们在开始学习 Python 后变得如此宠溺。你必须把它写成一个单独的函数?! GASP
【解决方案2】:

有点不合常规,但你可以让装饰器注册 func 并将任何循环变量绑定为默认参数:

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

for url in urls:
    @funcs.append
    def func(url=url):
        page = open(url)
        link = searchForLink(page)
        links.append(link)

【讨论】:

  • “非常规”,至少可以这么说。仍然,出奇的美丽;)
  • 我认为函数func 每次都会被覆盖。所以我只剩下最后一个,即处理最后一个 url 的函数。我不会吗?
  • +1:这真是太聪明了! @Guy:默认参数是在定义时评估的,所以绑定是好的。
【解决方案3】:

创建函数只有两种方法:deflambda。 Lambda 用于小型函数,因此它们可能不太适合您的情况。但是,如果你真的想要,你可以将两个 lambdas 封装在一起:

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

对我来说有点过于口齿不清了。

【讨论】:

    【解决方案4】:

    输了&lt;STATEMENT&gt;(funcs):

    编辑:

    我的意思是:你为什么要这样做?为什么要为每个页面定义一个新函数?为什么不这样做呢?

    urls = [many urls]
    links = []
    for url in urls:
        page = open(url)
        link = searchForLink(page)
        links.append(link) 
    

    【讨论】:

    • 来吧,伙计……这只是一个例子。当然你可以提出你的建议,但这不是我的问题。
    • 嗯。我投了+1,然后虽然“稍等一下”并且没有投票,但现在我决定投+1,但我不能。到目前为止,这确实是唯一有意义的答案。
    • @Guy:是的。它告诉您如何以不包含您不想拥有的额外函数调用的方式重写代码。
    • 当然可以。你真正问的唯一问题是“想法?”这个不错。它比您的工作示例更干净,并且它没有修改未传递给函数的变量的函数。如果您要查找的内容不适合您,请在您的问题中明确说明。
    【解决方案5】:

    你不应该使用“with”来做到这一点(尽管,鉴于它是 Python,你几乎肯定可以使用一些奇怪的副作用和 Python 的动态性)。

    Python 中“with”的目的是,as described in the docs,“用上下文管理器定义的方法包装块的执行。这允许常见的 try...except...finally 使用模式被封装方便重复使用。”

    我认为您将 Python 的“with”与 Javascript/VisualBasic“with”混淆了,它们在外观上可能相似,但实际上并不相关。

    【讨论】:

      【解决方案6】:

      好老itertools

      from itertools import imap
      links.extend(imap(searchForLink, imap(open, urls)))
      

      不过,也许你更喜欢functional

      from functional import *
      funcs = [partial(compose(compose(links.append, searchForLink), open), url) for url in urls]
      for func in funcs: func()
      

      我认为为with 使用创建一个类不值得:创建__enter____exit__ 比编写一个辅助函数要多。

      【讨论】:

      • 这太疯狂了。最好的答案 - 谢谢。我想我确实了解with 的作用。我希望将def func(): 粘贴在__enter__ 中,并将funcs.append(func) 粘贴在__exit__ 中。但是,显然,这不会那么容易。
      【解决方案7】:

      您可能会更好地使用生成器来实现您所追求的延迟计算。

      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,而不是一次全部查找(看起来您正在努力避免)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-10-27
        • 1970-01-01
        • 2012-10-22
        • 1970-01-01
        • 2023-03-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多