16

I'm switching from Ruby to Python for a project. I appreciate the fact that Python has first-class functions and closures, so this question ought to be easy. I just haven't figured out what is idiomatically correct for Python:

In Ruby, I could write:

def with_quietude(level, &block)
  begin
    saved_gval = gval
    gval = level
    yield
  ensure
    gval = saved_gval
  end
end

and call it like this:

with_quietude(3) {
  razz_the_jazz
  begin_the_beguine
}

(Note: I'm not asking about Python try/finally handling nor about saving and restoring variables -- I just wanted a non-trivial example of wrapping a block inside some other code.)

update

Or, since some of the answers are getting hung up on the global assignments in the previous example when I'm really asking about closures, what if the call was as follows? (Note that this doesn't change the definition of with_quietude):

def frumble(x)
  with_quietude {
    razz_the_jazz(x)
    begin_the_beguine(2 * x)
  }
end

How would you implement something similar in Python (and not get laughed at by the Python experts)?

4

3 回答 3

13

更深入地研究 ruby​​ 的产量,看起来你想要这样的东西contextlib.contextmanager

from contextlib import contextmanager

def razz_the_jazz():
    print gval

@contextmanager
def quietude(level):
    global gval
    saved_gval = gval
    gval = level

    try:
        yield
    finally:
        gval = saved_gval

gval = 1

with quietude(3):
     razz_the_jazz()

razz_the_jazz()

该脚本输出:

3
1

表明我们的上下文管理器确实gval在全局命名空间中重置。当然,我不会使用这个上下文管理器,因为它只适用于全局命名空间。(例如,它不适用于函数中的本地人)。

这基本上是对赋值如何创建对对象的新引用的限制,并且您永远不能通过直接赋值来改变对象。(改变对象的唯一方法是分配给它的一个属性或通过__setitem__a[x] = whatever))

于 2013-04-30T14:08:57.457 回答
4

如果您来自 Ruby,请注意:所有 python 'def' 基本上与 ruby​​ 'proc' 相同。

Python 没有 ruby​​ 的“def”等价物

通过在调用函数的范围内定义自己的函数,您可以获得与您所要求的非常相似的行为

def quietude(level, my_func):
    saved_gval = gval
    gval = level
    my_func()

def my_func():
  razz_the_jazz()
  begin_the_beguine()

quietude(3, my_func)

---- 编辑:要求提供更多信息: -----

Python 的 lambda 被限制在一行,所以它们不像 ruby​​ 那样灵活。

要传递带参数的函数,我建议使用部分函数,​​请参见以下代码:

import functools

def run(a, b):
    print a
    print b

def runner(value, func):
    func(value)

def start():
    s = functools.partial(run, 'first')
    runner('second', s)

---- 编辑 2 更多信息 ----

Python 函数仅在添加 '()' 时才会被调用。这与 '()' 是可选的 ruby​​ 不同。下面的代码在 start() 中运行“b_method”,在 run() 中运行“a_method”

def a_method():
    print 'a_method is running'
    return 'a'

def b_method():
    print 'b_method is running'
    return 'b'

def run(a, b):
    print a()
    print b

def start():
    run(a_method, b_method())
于 2013-04-30T14:36:25.403 回答
1

我喜欢 mgilson 给出的答案,所以它得到了检查。对于来自 Ruby 世界的人来说,这只是对 @contextmanager 功能的一个小扩展。

gval = 0

from contextlib import contextmanager

@contextmanager
def quietude(level):
    global gval
    saved_gval = gval
    gval = level
    try:
        yield
    finally:
        gval = saved_gval

def bebop(x):
  with quietude(3):
    print "first", x*2, "(gval =", gval, ")"
    print "second", x*4, "(gval =", gval, ")"

bebop(100)
bebop("xxxx")

这打印出来:

first 200 (gval = 3 )
second 400 (gval = 3 )
first xxxxxxxx (gval = 3 )
second xxxxxxxxxxxxxxxx (gval = 3 )

这表明 范围内的所有东西都可以with访问词法封闭的变量,并且或多或少地表现出来自 Ruby 世界的人所期望的方式。

好东西。

于 2013-04-30T17:23:06.380 回答