Python: 匿名函数的替代方案

6 投票
3 回答
8148 浏览
提问于 2025-04-17 10:49

Python不支持复杂的匿名函数。那么有什么好的替代方案呢?比如:

class Calculation:
    def __init__(self, func):
        self.func = func

    def __call__(self, data):
        try:
        # check if the value has already been calculated
        # if it has, it would be cached under key = self.func
            return data[self.func]
        except KeyError:
            pass # first-time call; calculate and cache the values
        data[self.func] = self.func(data)
        return data[self.func]

# with a simple function, which can be represented using lambda, this works great
f1 = Calculation(lambda data : data['a'] * data['b'])

# with a complicated function, I can do this:
def f2_aux:
   # some complicated calculation, which isn't suitable for a lambda one-liner
f2 = Calculation(f2_aux) 

这样设计合理吗?

如果合理的话,有没有办法避免为我在模块中定义的每个函数都写一个丑陋的f*_aux?

更新:

使用示例:

d = {'a' : 3, 'b' : 6}

# computes 3 * 6
# stores 18 in d under a key <function <lambda> at ...>
# returns 18
f1(d)

# retrieves 18 from d[<function <lambda> at ...>]
# returns 18, without having to recalculate it
f1(d)

更新:

为了让我更好理解,我添加了一个使用内部函数的版本。

def memoize(func):
    def new_func(data):
        try:
        # check if the value has already been calculated
        # if it has, it would be cached under key = self.func
            return data[func]
        except KeyError:
            pass # first-time call; calculate and cache the values
        data[func] = func(data)
        return data[func]
    return new_func

@memoize
def f1(data):
  return data['a'] * data['b']

3 个回答

1

闭包可以是写类的一种简洁替代方法,就像你例子中的那样。这个技巧就是在一个函数里面再定义一个函数。里面的函数可以访问外面函数里的变量。在Python 3中,nonlocal关键字让你可以修改那个变量。在Python 2中,你需要用一个可变的值来作为非局部变量,这样才能在里面的函数中更新它。

关于匿名函数的问题,语言故意让你在处理比lambda更复杂的事情时,回到使用def来定义函数。

4

匿名函数的替代品就是非匿名函数。匿名函数在定义它的地方是匿名的,但实际上并不完全匿名,因为如果它真的匿名,你就无法使用它。

在Python中,你可以用lambda语句来创建匿名函数。例如,你可以这样做:

output = mysort(input, lambda x: x.lastname)

这个lambda会创建一个函数,但在本地环境中这个函数没有名字,它自己的名字只是'<lambda>'。不过,如果我们看看mysort,它的定义应该是这样的:

def mysort(input, getterfunc):
    blahblahblah

从这里我们可以看到,在这个上下文中,这个函数根本不是匿名的。它有一个名字,叫getterfunc。对于这个函数来说,传入的函数是否匿名并不重要。这两种方式都能正常工作,在所有重要的方面是完全等价的:

def get_lastname(x):
    return x.lastname

output = mysort(input, get_lastname)

当然,这样写代码会多一些,但并不会变得更慢。在Python中,匿名函数其实就是普通函数的一种语法糖。

一个真正的匿名函数是:

lambda x: x.lastname

但是因为我们没有把这个生成的函数赋值给任何东西,所以我们没有给这个函数一个名字,这样我们就无法使用它。所有真正的匿名函数都是无法使用的。

因此,如果你需要一个不能用lambda的函数,就把它写成普通函数。它在任何有意义的情况下都不可能是匿名的,那为什么还要费劲去做匿名呢?使用lambda的好处在于你想要一个小的单行函数,而不想浪费空间去定义一个完整的函数。它们是否匿名其实并不重要。

5

其实你不需要用到匿名函数。而且,记忆化(memoization)这个技术已经有更好的实现方式了,所以你可能没必要自己去写一个。

不过为了回答这个问题:你可以把你的类当作一个装饰器来使用。

@Calculation
def f2():
    ...

这段代码简单地定义了一个函数,把它包裹在 Calculation 中,并把结果存储为 f2

装饰器的语法其实可以理解为等同于:

_decorator = Calculation # a fresh identifier
# not needed here, but in other cases (think properties) it's useful
def f2():
    ...
f2 = _decorator(f2)

撰写回答