python:匿名函数的替代品

Posted

技术标签:

【中文标题】python:匿名函数的替代品【英文标题】:python: alternative to anonymous functions 【发布时间】:2012-02-16 02:21:38 【问题描述】:

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* 的 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']

【问题讨论】:

不,这不是一个合理的设计。你似乎在做记忆(这与匿名函数无关),而且做得很糟糕。 f1 = lambda a: expression 始终与 def f1(a): return expression 完全相同。 This is a good memoization decorator to study。顺便说一句,@decorator def function: pass 总是与def function: pass; function = decorator(function) 完全相同 糟糕的是你的记忆器坏了。结果缓存 data 也是您的函数的参数... @FrancisAvila:我打算在字典元素data[f] 中缓存f(data) 的值。我知道传递给我的 data 永远不会使用这样的键 - 也不应该发生与另一个函数的冲突,因为另一个函数的值将缓存在另一个键下。看起来它有效(技术上);问题只是在不必要的混乱代码中吗? @FrancisAvila 好吧,data(比如字典)被传递给函数:f1(data)。只要调用者需要,调用者就会保持data 处于活动状态,如果他们再次调用f1(data),缓存就会启动。(至少我希望它是这样工作的。) memoization 是不透明的:你在 memoized 函数(单参数字典)上强加了一个接口,并要求调用代码与参数和所有缓存值共享相同的命名空间。如果调用代码要更改示例中的“a”键,那么 data 中的每个功能键现在都有一个过时的缓存值! 【参考方案1】:

您不需要匿名函数。此外,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)

【讨论】:

我是否理解正确,虽然通常装饰器是将一个函数作为参数并返回另一个函数的函数 - 在这种情况下,class Calculation 适合这个角色,因为它的构造函数签名及其__call__ 方法? @max:装饰器没有任何限制,当然它们必须是可调用的,并且为了有用它们应该有一个有意义的返回值。类可通过__new__/__init__ 调用,并且由于类的实例是可调用的(尽管__call__),因此返回值很有用。 @max “可调用”,用 Python 的说法。【参考方案2】:

匿名函数的替代方法是非匿名函数。匿名函数仅在定义它的上下文中是匿名的。但它并不是真正的匿名,因为那时你无法使用它。

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

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

lambda 将创建一个函数,但该函数在本地空间中没有名称,它自己的名称只是'&lt;lambda&gt;'。但是,如果我们查看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 非常有用。他们是匿名的无关紧要。

【讨论】:

谢谢.. 我想我只是想节省编写的代码量(以及潜在的拼写错误的数量),我同意这不应该是一个主要问题。 【参考方案3】:

闭包可以是编写类(例如您的示例中的类)的简洁替代方案。该技术涉及将 def 放入另一个 def 中。内部函数可以访问封闭函数中的变量。在 Python 3 中,nonlocal 关键字为您提供对该变量的写访问权。在 Python 2 中,您需要为非局部变量使用可变值,以便能够从内部函数对其进行更新。

关于匿名函数的问题,该语言故意将您推回使用 def 处理任何比 lambda 无法处理的更复杂的事情。

【讨论】:

您的意思类似于我在上次更新中添加的问题?

以上是关于python:匿名函数的替代品的主要内容,如果未能解决你的问题,请参考以下文章

python编程_匿名函数_lambda

Python匿名函数

python基础——匿名函数及递归函数

python匿名函数

python(匿名函数)

python函数 | 匿名函数