【问题标题】:Finding Functions Defined in a with: Block查找在 with: 块中定义的函数
【发布时间】:2009-08-10 16:38:03
【问题描述】:

这是来自Richard Jones' Blog的一些代码:

with gui.vertical:
    text = gui.label('hello!')
    items = gui.selection(['one', 'two', 'three'])
    with gui.button('click me!'):
        def on_click():
            text.value = items.value
            text.foreground = red

我的问题是:他到底是怎么做到的?上下文管理器如何访问 with 块内的范围?这是一个尝试解决此问题的基本模板:

from __future__ import with_statement

class button(object):
  def __enter__(self):
    #do some setup
    pass

  def __exit__(self, exc_type, exc_value, traceback):
    #XXX: how can we find the testing() function?
    pass

with button():
  def testing():
    pass

【问题讨论】:

    标签: python scope with-statement contextmanager


    【解决方案1】:

    这是一种方法:

    from __future__ import with_statement
    import inspect
    
    class button(object):
      def __enter__(self):
        # keep track of all that's already defined BEFORE the `with`
        f = inspect.currentframe(1)
        self.mustignore = dict(f.f_locals)
    
      def __exit__(self, exc_type, exc_value, traceback):
        f = inspect.currentframe(1)
        # see what's been bound anew in the body of the `with`
        interesting = dict()
        for n in f.f_locals:
          newf = f.f_locals[n]
          if n not in self.mustignore:
            interesting[n] = newf
            continue
          anf = self.mustignore[n]
          if id(newf) != id(anf):
            interesting[n] = newf
        if interesting:
          print 'interesting new things: %s' % ', '.join(sorted(interesting))
          for n, v in interesting.items():
            if isinstance(v, type(lambda:None)):
              print 'function %r' % n
              print v()
        else:
          print 'nothing interesting'
    
    def main():
      for i in (1, 2):
        def ignorebefore():
          pass
        with button():
          def testing(i=i):
            return i
        def ignoreafter():
          pass
    
    main()
    

    编辑:将代码拉长一点,添加一些解释...:

    __exit__ 捕获调用者的本地人很容易——更棘手的是避免那些在with 块之前已经定义的本地人,这就是为什么我将@ 添加到主要的两个本地函数中987654325@ 应该忽略。我对这个解决方案不是 100% 满意,它看起来有点复杂,但是我无法使用 ==is 进行正确的相等性测试,所以我采用了这种相当复杂的方法。

    我还添加了一个循环(以更强烈地确保正确处理之前/内部/之后的defs)和一个类型检查和函数调用,以确保testing 的正确化身是被识别的那个(一切似乎都工作正常)——当然,编写的代码只有在with 中的def 用于不带参数的可调用函数时才有效,使用@987654332 获得签名并不难@ 来避免这种情况(但由于我只是为了检查是否识别了正确的函数对象而进行调用,所以我没有为最后的改进而烦恼;-)。

    【讨论】:

    • 不客气!这是一个有趣的问题,所以 tx 提出来;-)。
    • 我发布了一篇关于使用你给我的代码的博客文章,如果你有兴趣的话:billmill.org/multi_line_lambdas.html
    【解决方案2】:

    回答你的问题,没错,就是框架内省。

    但我要创建的语法是

    with gui.vertical:
        text = gui.label('hello!')
        items = gui.selection(['one', 'two', 'three'])
        @gui.button('click me!')
        class button:
            def on_click():
                text.value = items.value
                text.foreground = red
    

    在这里,我将gui.button 实现为一个装饰器,它在给定一些参数和事件的情况下返回按钮实例(尽管现在在我看来button = gui.button('click me!', mybutton_onclick 也很好)。

    我也会保留gui.vertical,因为它可以在没有自省的情况下实现。我不确定它的实现,但它可能涉及设置gui.direction = gui.VERTICAL,以便gui.label() 和其他人使用它来计算他们的坐标。

    现在当我看到这个时,我想我会尝试一下语法:

        with gui.vertical:
            text = gui.label('hello!')
            items = gui.selection(['one', 'two', 'three'])
    
            @gui.button('click me!')
            def button():
                text.value = items.value
                foreground = red
    

    (这个想法类似于标签由文本组成,按钮由文本和功能组成)

    【讨论】:

    • 但是为什么要使用“with gui.vertical”呢?它需要执行相同的堆栈内省才能访问其中的文本、项目和按钮。我确定您会执行以下操作: class MyLayout(gui.Vertical): text = gui.label('hello!') #etc 对吗?无论如何,我很清楚这是对 with 块的严重非标准滥用。我只是想知道他是怎么做到的。我希望你至少看到这是对 with 块的一种很酷的滥用:)
    • 我将with gui.vertical 读作“不创建元素,但确保在此上下文中创建的所有元素都从当前点垂直计算它们的坐标”。没有自省。
    猜你喜欢
    • 2010-11-09
    • 2012-06-21
    • 2012-04-07
    • 1970-01-01
    • 1970-01-01
    • 2018-04-15
    • 1970-01-01
    • 2021-05-17
    相关资源
    最近更新 更多