2009-08-10 190 views
9

下面是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块内的作用域?下面是试图弄清楚这一点,一个基本的模板:

回答

11

这里有一种方法:

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块之前的当地人,这就是为什么我添加到主要两个with应该忽略的本地功能。我对此解决方案并不满意,但看起来有点复杂,但我无法通过==is获得正确的平等测试,所以我采用了这种相当复杂的方法。

我还添加了一个循环(为了更加确定def的正确处理之前/之内/之后)以及类型检查和函数调用,以确保testing正确的化身是一个(一切似乎工作正常) - 当然,只有在with内的def用于可以不带参数调用的函数时,写入的代码才起作用,但用inspect来签署该签名并不难(因为我只是为了检查是否识别出正确的功能对象而进行的调用,我没有为这最后的改进而烦恼;-)。

+0

很可爱,非常感谢。 – llimllib 2009-08-10 18:53:12

+1

不客气!是一个有趣的问题来解决,所以TX摆姿势;-)。 – 2009-08-10 21:25:22

+1

我发表了一篇关于使用你给我的代码的博客文章,以防你感兴趣:http://billmill.org/multi_line_lambdas.html – llimllib 2009-08-21 02:54:21

1

要回答你的问题,是的,这是框架反思。

但语法我将创建做同样的事情是

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 

(的想法是,类似的标签是怎么做出来的文字,按钮是做出来的文字和功能)

+0

但为什么使用“与gui.vertical”?它需要进行相同的堆栈反思才能访问其中的文本,项目和按钮。我敢肯定,你做这样的事情: 类MyLayout(gui.Vertical): 文本= gui.label( '你好!') #etc 吧? 无论如何,我很清楚这是一个非常严重的非标准滥用with block。我只是想知道他是如何做到的。 我希望你至少可以看到这是一个非常酷的使用block :) – llimllib 2009-08-10 19:49:09

+0

我把'gui.vertical'读为“不创建任何元素,但要确保在此上下文中创建的所有元素都从当前垂直计算它们的坐标点”。没有反思。 – 2009-08-11 17:01:55