【问题标题】:lambda returns lambda in pythonlambda 在 python 中返回 lambda
【发布时间】:2010-12-06 00:09:01
【问题描述】:

我很少会在 python 中遇到一些使用匿名函数返回匿名函数的代码...?

很遗憾,我手头上找不到示例,但它通常采用如下形式:

g = lambda x,c: x**c lambda c: c+1

为什么有人会这样做?也许你可以举一个有意义的例子(我不确定我做的那个有什么意义)。

编辑:这是一个例子:

swap = lambda a,x,y:(lambda f=a.__setitem__:(f(x,(a[x],a[y])),
       f(y,a[x][0]),f(x,a[x][1])))()

【问题讨论】:

  • 找到并展示一个真实的例子;那个语法不正确。
  • 产生 SyntaxError: invalid syntax。没有意义的第二个 lambda
  • 为什么有人会这样做?答:激怒别人?
  • 另外,交换列表的两个元素的更清晰的方法是使用元组解包:x[1], x[2] = x[2], x[1]
  • 一般来说:lambdas 应该用于简单的表达式。很有可能,返回任何返回 lambda 的东西并不简单。

标签: python function anonymous


【解决方案1】:

你可以使用这样的结构来做currying

curry = lambda f, a: lambda x: f(a, x)

你可以这样使用它:

>>> add = lambda x, y: x + y
>>> add5 = curry(add, 5)
>>> add5(3)
8

【讨论】:

    【解决方案2】:
    swap = lambda a,x,y:(lambda f=a.__setitem__:(f(x,(a[x],a[y])),
           f(y,a[x][0]),f(x,a[x][1])))()
    

    看到末尾的 () 了吗?内部 lambda 不返回,它被调用。

    函数相当于

    def swap(a, x, y):
         a[x] = (a[x], a[y])
         a[y] = a[x][0]
         a[x] = a[x][1]
    

    但是让我们假设我们想在 lambda 中执行此操作。我们不能在 lambda 中使用赋值。但是,我们可以调用__setitem__ 来达到同样的效果。

    def swap(a, x, y):
         a.__setitem__(x, (a[x], a[y]))
         a.__setitem__(y, a[x][0])
         a.__setitem__(x, a[x][1])
    

    但是对于 lambda,我们只能有一个表达式。但是由于这些是函数调用,我们可以将它们包装在一个元组中

    def swap(a, x, y):
         (a.__setitem__(x, (a[x], a[y])),
         a.__setitem__(y, a[x][0]),
         a.__setitem__(x, a[x][1]))
    

    但是,所有这些 __setitem__ 都让我失望了,所以让我们把它们排除在外:

    def swap(a, x, y):
         f = a.__setitem__
         (f(x, (a[x], a[y])),
         f(y, a[x][0]),
         f(x, a[x][1]))
    

    Dagnamit,我不能再添加另一个作业了!我知道让我们滥用默认参数。

    def swap(a, x, y):
         def inner(f = a.__setitem__):
             (f(x, (a[x], a[y])),
             f(y, a[x][0]),
             f(x, a[x][1]))
         inner()
    

    好的,让我们切换到 lambda:

    swap = lambda a, x, y: lambda f = a.__setitem__: (f(x, (a[x], a[y])), f(y, a[x][0]),  f(x, a[x][1]))()
    

    这让我们回到原来的表达方式(加/减错别字)

    所有这些都回到了这个问题:为什么?

    函数应该已经实现了

    def swap(a, x, y):
        a[x],a[y] = a[y],a[x]
    

    原作者竭尽全力使用 lambda 而不是函数。可能是出于某种原因他不喜欢嵌套函数。我不知道。我要说的是它的错误代码。 (除非有什么神秘的理由。)

    【讨论】:

    • 我喜欢 @Winston 在标记中思考、编码和自言自语的方式。
    【解决方案3】:

    它对于临时占位符很有用。假设你有一个装饰器工厂:

    @call_logger(log_arguments=True, log_return=False)
    def f(a, b):
        pass
    

    你可以暂时替换成

    call_logger = lambda *a, **kw: lambda f: f
    

    如果它间接返回一个 lambda 也很有用:

    import collections
    collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(int)))
    

    这对于在 Python 控制台中创建可调用工厂也很有用。

    并且仅仅因为某事是可能的并不意味着你必须使用它。

    【讨论】:

      【解决方案4】:

      前几天我做了类似的事情来禁用单元测试套件中的测试方法。

      disable = lambda fn : lambda *args, **kwargs: None
      
      @disable
      test_method(self):
          ... test code that I wanted to disable ...
      

      以后很容易重新启用它。

      【讨论】:

        【解决方案5】:

        这可以用来拉出一些常见的重复代码(在python中当然还有其他方法可以实现)。

        也许您正在编写一个记录器,并且您需要将级别添加到日志字符串。你可能会这样写:

        import sys
        prefixer = lambda prefix: lambda message: sys.stderr.write(prefix + ":" + message + "\n")
        log_error = prefixer("ERROR")
        log_warning = prefixer("WARNING")
        log_info = prefixer("INFO")
        log_debug = prefixer("DEBUG")
        
        log_info("An informative message")
        log_error("Oh no, a fatal problem")
        

        这个程序打印出来

           INFO:An informative message
           ERROR:Oh no, a fatal problem
        

        【讨论】:

          【解决方案6】:

          最常见的情况是——至少在我遇到并且我自己编写的代码中——用来“冻结”一个变量,使其具有在创建 lambda 函数时的值。否则,nonlocals 变量会引用它们存在的范围内的变量,这有时会导致不希望的结果。

          例如,如果我想创建一个包含十个函数的列表,每个函数都是从 0 到 9 的标量的乘数。有人可能会想这样写:

          >>> a = [(lambda j: i * j) for i in range(10)]
          >>> a[9](10)
          90
          

          谁,如果你想使用任何其他分解函数,你会得到相同的结果:

          >>> a[1](10)
          90
          

          这是因为在创建 lambda 时,lambda 内部的“i”变量没有被解析。相反,Python 在“for”语句中保留对“i”的引用——在它创建的范围内(这个引用保存在 lambda 函数闭包中)。当 lambda 被执行时,变量被计算,它的值是它在那个范围内的最后一个值。

          当一个人像这样使用两个嵌套的 lambdas 时:

          >>> a = [(lambda k: (lambda j: k * j))(i) for i in range(10)]
          

          “i”变量在“for”循环的执行过程中被计算。它的值被传递给“k” - 并且“k”用作我们要分解的乘法器函数中的非局部变量。对于 i 的每个值,将有一个不同的封闭 lambda 函数实例,以及一个不同的“k”变量值。

          所以,原来的意图是可以实现的:

          >>> a = [(lambda k: (lambda j: k * j))(i) for i in range(10)]
          >>> a[1](10)
          10
          >>> a[9](10)
          90
          >>>
          

          【讨论】:

          • 为什么不改用:a = [(lambda k, i=i: i*j) for j in range(10)]?
          【解决方案7】:

          它可以用来实现更延续/蹦床风格的编程,

          Continuation-passing style

          【讨论】:

            【解决方案8】:

            基本上,你可以修改函数而不是值

            我最近偶然发现的一个例子:计算近似导数(作为函数)并将其用作另一个地方的输入函数。

            dx = 1/10**6    
            ddx = lambda f: lambda x: (f(x + dx) - f(x))/dx
            
            f = lambda x: foo(x)
            newton_method(func=ddx(f), x0=1, n=10)
            

            【讨论】:

              猜你喜欢
              • 2012-09-20
              • 2018-05-12
              • 2015-01-08
              • 1970-01-01
              • 2019-09-19
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多