匿名函数在python中引用局部变量

时间:2012-03-27 00:56:28

标签: python variables callback scope

如何在python中定义匿名函数,其中bahaviour应该在definiton-time取决于局部变量的值,并且还接受参数

示例:

def callback(val1, val2):
   print "{0} {1}".format(val1, val2)

i = 0
f0 = lambda x: callback(i, x)
i = 1
f1 = lambda x: callback(i, x)

f0(8) # prints "1, 8: but I'd like "0, 8" (value of 'i' when f0 was defined)
f1(8) # prints "1, 8"

如果没有将我的回调包装在自己的类中,这样的事情是否可能?

4 个答案:

答案 0 :(得分:8)

使用functools.partial

的python中的闭包
from functools import partial

i = 0
f0 = partial(callback, i)
i = 1
f1 = partial(callback, i)

f0()
# 0
f1()
# 1

partial就像一个lambda,但在那一刻将值包装到arg中。在调用时不评估它。

仅包装部分参数

是partial将允许你包装任意数量的参数,然后剩下的args和kwargs可以传递给生成的部分对象,这样它就像调用原始包装函数一样......

def callback(val1, val2):
   print "{0} {1}".format(val1, val2)

i = 0
x = 8
f0 = partial(callback, i)
f0(x)
# 0 8

基本上,您已将callback(val1, val2)包裹到callback(val2)中,val1已被包含为关闭。

使用lambda

的类似效果的示例

如果您真的想看看如何使用lambda闭包来实现这一点,您可以看到为什么它变得丑陋而且部分是首选......

f0 = (lambda val1: lambda val2: callback(val1, val2))(i)

您必须将scope变量包装到外部函数作用域中,然后在内部lambda函数中引用该作用域。育。

异常回溯:部分vs lambda vs嵌套函数

随着其他答案的涌入,我想我会概述使用partial而不是lambda或内/外函数闭包的另一个原因。请记住,我的意思是功能关闭。 functools.partial修复了当你的包装函数引发异常时你将得到的回溯......

考虑这个版本将除以零:

def callback(val1, val2):
    return val1 / val2

正常的外部/内部关闭

def wrapper(fn, val1):
    def wrapped(val2):
            return fn(val1, val2)
    return wrapped

f0 = wrapper(callback, i)
f0(0)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in wrapped
  File "<stdin>", line 2, in callback
ZeroDivisionError: integer division or modulo by zero

lambda closure

f0 = (lambda val1: lambda val2: callback(val1, val2))(i)
f0(0)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
  File "<stdin>", line 2, in callback
ZeroDivisionError: integer division or modulo by zero

现在为functools.partial

f0 = partial(callback, i)
f0(0)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in callback
ZeroDivisionError: integer division or modulo by zero

答案 1 :(得分:1)

你可以通过functools.partial来实现这个目标:

f0 = partial(callback, i)

答案 2 :(得分:0)

您可以创建一个创建功能的功能。如其他人所提到的,partial是另一种选择。

def repeater(s):
  def anon():
      print s
  return anon

greet = repeater("Hello, World")
greet()

答案 3 :(得分:0)

虽然在这种情况下使用functools.partial是更好的解决方案,但您也可以创建显式闭包:

def callback_generator(val):
    def callback():
        return val
    return callback
i = 0
f0 = callback1(i)
i = 1
f1 = callback1(i)

这也可以用lambdas完成,如下面的评论所示。