代码之家  ›  专栏  ›  技术社区  ›  Ray

在python:lambda函数或嵌套函数(“def”)中使用哪个更可取?

  •  83
  • Ray  · 技术社区  · 16 年前

    我主要使用lambda函数,但有时使用似乎提供相同行为的嵌套函数。

    以下是一些简单的例子,如果在另一个函数中找到了其中一个函数,那么它们在功能上执行相同的操作:

    λ函数

    >>> a = lambda x : 1 + x
    >>> a(5)
    6
    

    嵌套函数

    >>> def b(x): return 1 + x
    
    >>> b(5)
    6
    

    使用一个比另一个好吗?(表演)?可读性?局限性?一致性?等)

    这甚至重要吗?如果没有,那么这是否违反了蟒蛇原理:

    “There should be one—and preferably only one—obvious way to do it” .

    16 回复  |  直到 7 年前
        1
  •  87
  •   nosklo    16 年前

    如果需要分配 lambda 对于一个名称,使用 def 相反。 DEF 对于一个作业来说,S只是句法上的糖分,所以结果是一样的,而且它们更加灵活和可读。

    兰姆达 S可用于 使用一次,扔掉 没有名称的函数。

    然而,这个用例是非常罕见的。很少需要传递未命名的函数对象。

    建筑材料 map() filter() 需要函数对象,但是 列表推导式 生成器表达式 通常比这些函数更易读,可以覆盖所有用例,而不需要lambda。

    对于真正需要小函数对象的情况,应该使用 operator 模块功能,比如 operator.add 而不是 lambda x, y: x + y

    如果你还需要一些 兰姆达 不包括,你可以考虑写 DEF ,只是为了提高可读性。如果函数比 操作人员 模块,A DEF 可能更好。

    所以,现实世界很好 兰姆达 用例非常罕见。

        2
  •  27
  •   Thomas Vander Stichele    15 年前

    实际上,对我来说,有两个不同之处:

    第一个问题是他们做了什么,返回了什么:

    • def是一个关键字,它不返回任何内容,并在本地命名空间中创建“name”。

    • lambda是一个返回函数对象的关键字,不会在本地命名空间中创建“name”。

    因此,如果需要调用一个接受函数对象的函数,在一行Python代码中实现这一点的唯一方法就是使用lambda。没有与DEF相同的功能。

    在某些框架中,这实际上非常常见;例如,我使用 Twisted 做了很多这样的事

    d.addCallback(lambda result: setattr(self, _someVariable, result))
    

    是相当常见的,与兰姆达斯更简洁。

    第二个区别是关于允许实际函数做什么。

    • 用“def”定义的函数可以包含任何python代码
    • 用lambda定义的函数必须计算为表达式,因此不能包含print、import、raise等语句。

    例如,

    def p(x): print x
    

    按预期工作,而

    lambda x: print x
    

    是语法错误。

    当然,还有解决办法-替代 print 具有 sys.stdout.write import 具有 __import__ . 但在这种情况下,通常你最好使用函数。

        3
  •  19
  •   Aaron Hall    7 年前

    In this interview, 吉多·范·罗森说他希望自己不要让“lambda”进入巨蟒:

    问:你最不喜欢Python的哪些特性?

    有时候我接受捐款太快了,后来意识到这是个错误。一个例子是一些函数编程特性,比如lambda函数。lambda是一个允许您创建小型匿名函数的关键字;内置函数(如map、filter和reduce)在序列类型(如list)上运行函数。

    实际上,结果不太好。python只有两个作用域:本地作用域和全局作用域。这使得编写lambda函数很痛苦,因为您经常希望访问lambda定义的范围中的变量,但由于这两个范围,您不能访问。有办法解决这个问题,但这有点像是一个拼凑。在Python中,通常只使用for循环,而不必使用lambda函数。地图和朋友只有当已经有了一个内置的函数可以满足您的需要时才能很好地工作。

    imho,iambdas有时很方便,但通常以牺牲可读性为代价。你能告诉我这是干什么的吗?

    str(reduce(lambda x,y:x+y,map(lambda x:x**x,range(1,1001))))[-10:]
    

    我写了它,花了我一分钟才弄明白。这是来自Project Euler的-我不会说哪个问题,因为我讨厌扰流器,但它只运行0.124秒:)

        4
  •  9
  •   Andy Hayden    11 年前

    对于n=1000,有时需要调用一个函数与lambda:

    In [11]: def f(a, b):
                 return a * b
    
    In [12]: g = lambda x, y: x * y
    
    In [13]: %%timeit -n 100
    for a in xrange(n):
      for b in xrange(n):
        f(a, b)
       ....:
    100 loops, best of 3: 285 ms per loop
    
    In [14]: %%timeit -n 100
    for a in xrange(n):
      for b in xrange(n):
        g(a, b)
       ....:
    100 loops, best of 3: 298 ms per loop
    
    In [15]: %%timeit -n 100
    for a in xrange(n):
      for b in xrange(n):
        (lambda x, y: x * y)(a, b)
       ....:
    100 loops, best of 3: 462 ms per loop
    
        5
  •  6
  •   Dan Lenski    16 年前

    我同意Nosklo的建议:如果需要为函数命名,请使用 def . 我保留 lambda 函数,用于将一小段代码传递给另一个函数的情况,例如:

    a = [ (1,2), (3,4), (5,6) ]
    b = map( lambda x: x[0]+x[1], a )
    
        6
  •  5
  •   Pi Marillion    9 年前

    性能:

    使用创建函数 lambda 稍快 而不是用 def . 差异是由于 DEF 在局部变量表中创建名称项。生成的函数具有相同的执行速度。


    可读性:

    对于大多数Python用户来说,lambda函数的可读性稍差,但在某些情况下,它也更简洁。考虑从使用非功能例行程序转换为功能例行程序:

    # Using non-functional version.
    
    heading(math.sqrt(v.x * v.x + v.y * v.y), math.atan(v.y / v.x))
    
    # Using lambda with functional version.
    
    fheading(v, lambda v: math.sqrt(v.x * v.x + v.y * v.y), lambda v: math.atan(v.y / v.x))
    
    # Using def with functional version.
    
    def size(v):
        return math.sqrt(v.x * v.x + v.y * v.y)
    
    def direction(v):
        return math.atan(v.y / v.x)
    
    deal_with_headings(v, size, direction)
    

    如你所见, 兰姆达 从你只需要添加的意义上来说,版本更短更容易。 lambda v: 转换为原始的非功能版本以转换为功能版本。它也更加简洁。但是请记住,许多Python用户会被lambda语法混淆,因此,在长度和实际复杂性方面丢失的内容可能会在其他编码人员的混淆中得到恢复。


    局限性:

    • 兰姆达 除非分配给变量名,否则函数只能使用一次。
    • 兰姆达 分配给变量名的函数与 DEF 功能。
    • 兰姆达 功能可能很难或不可能被酸洗。
    • DEF 必须仔细选择函数名,使其具有合理的描述性和唯一性,或者至少在范围内未使用。

    一致性:

    python主要避免使用函数式编程约定,而支持过程性和简单的目标语义。这个 兰姆达 操作员站在这个偏差的直接对比。此外,作为对已经流行的 DEF , the 兰姆达 函数为您的语法添加了多样性。有些人会认为这不太一致。


    现有功能:

    正如其他人所指出的,许多 兰姆达 在字段中可以由 operator 或其他模块。例如:

    do_something(x, y, lambda x, y: x + y)
    do_something(x, y, operator.add)
    

    在许多情况下,使用预先存在的函数可以使代码更具可读性。


    肾盂原理:应该有一个,最好只有一个明显的方法。

    类似于 single source of truth 教条。不幸的是,一个显而易见的实现IT原则的方法始终是对Python的渴望,而不是一个真正的指导原则。考虑一下Python中非常强大的数组理解。它们在功能上等同于 map filter 功能:

    [e for e in some_array if some_condition(e)]
    filter(some_array, some_condition)
    

    兰姆达 DEF 都一样。

    这是一个意见问题,但我想说的是,在Python语言中,任何用于一般用途的东西,如果不明显地破坏任何东西,都是“蟒蛇式的”足够了。

        7
  •  3
  •   apg    16 年前

    lambda的主要用途总是用于简单的回调函数,以及用于map、reduce、filter,后者需要一个函数作为参数。当列表理解成为规范时,允许添加如下内容:

    x = [f for f in range(1, 40) if f % 2]
    

    很难想象在日常使用中使用lambda的真实情况。因此,我会说,避免使用lambda并创建嵌套函数。

        8
  •  3
  •   SingleNegationElimination    13 年前

    lambda的一个重要限制是它们不能包含除表达式之外的任何内容。lambda表达式几乎不可能产生除小的副作用以外的任何东西,因为它不能有像 def ED函数。

    这就是说,Lua影响了我的编程风格,使我对匿名函数的广泛使用产生了影响,我把代码丢在了它们上面。除此之外,我倾向于将map/reduce看作抽象的操作符,在某种程度上,我不考虑列表理解或生成器,就好像我使用这些操作符显式地推迟了一个实现决策。

    编辑: 这是一个相当古老的问题,我对这件事的看法有所改变。

    首先,我对分配 lambda 表达式到变量;因为python有一个专门的语法(提示, DEF )除此之外,lambda的许多用法,即使它们没有名称,也有预定义的(和更有效的)实现。例如,所讨论的示例可以缩写为 (1).__add__ ,无需将其包装在 兰姆达 DEF . 许多其他常见的用途可以满足于 operator , itertools functools 模块。

        9
  •  3
  •   Jonathan Livni    11 年前

    虽然同意其他答案,但有时它更具可读性。下面是一个例子 lambda 在一个用例中,我经常遇到一个n维的 defaultdict .
    下面是一个例子:

    from collections import defaultdict
    d = defaultdict(lambda: defaultdict(list))
    d['Foo']['Bar'].append(something)
    

    我发现它比创建一个 def 对于第二维度。这对于更高的维度更为重要。

        10
  •  2
  •   Glushiator    9 年前

    我发现的一种羊羔肉的用途…在调试消息中。

    因为lambda可以被懒散地评估,所以您可以使用如下代码:

    log.debug(lambda: "this is my message: %r" % (some_data,))
    

    而不是昂贵的:

    log.debug("this is my message: %r" % (some_data,))
    

    它处理格式字符串,即使调试调用由于当前日志记录级别而不生成输出。

    当然,要使它按描述工作,正在使用的日志模块必须支持lambda作为“惰性参数”(正如我的日志模块所做的那样)。

    同样的想法也可以应用于任何其他延迟评估的情况,以创建随需应变的内容价值。

    例如,此自定义三元运算符:

    def mif(condition, when_true, when_false):
        if condition:
             return when_true()
        else:
             return when_false()
    
    mif(a < b, lambda: a + a, lambda: b + b)
    

    而不是:

    def mif(condition, when_true, when_false):
        if condition:
             return when_true
        else:
             return when_false
    
    mif(a < b, a + a, b + b)
    

    使用lambda时,只计算由条件选择的表达式,而不使用lambda时,两者都将被计算。

    当然,您可以简单地使用函数而不是lambda,但是对于短表达式,lambda更精简(c)。

        11
  •  2
  •   Aaron Hall    7 年前

    更可取的是:lambda函数或嵌套函数( def )?

    与正则函数相比,使用lambda有一个优点(它们是在表达式中创建的),还有几个缺点。因此,我更喜欢使用 DEF 关键字而不是lambda。

    第一点-它们是同一类型的物体

    lambda产生的对象类型与正则函数相同

    >>> l = lambda: 0
    >>> type(l)
    <class 'function'>
    >>> def foo(): return 0
    ... 
    >>> type(foo)
    <class 'function'>
    >>> type(foo) is type(l)
    True
    

    因为lambda是函数,所以它们是一流的对象。

    lambda和函数:

    • 可以作为参数传递(与正则函数相同)
    • 当在一个外部函数中创建时,就成为该外部函数局部变量的闭包。

    但默认情况下,lambda缺少一些函数通过完整函数定义语法获得的东西。

    兰巴的 __name__ '<lambda>'

    毕竟,lambda是匿名函数,所以它们不知道自己的名字。

    >>> l.__name__
    '<lambda>'
    >>> foo.__name__
    'foo'
    

    因此,lambda不能在其命名空间中以编程方式查找。

    这限制了某些事情。例如, foo 可以使用序列化代码查找,而 l 不能:

    >>> import pickle
    >>> pickle.loads(pickle.dumps(l))
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    _pickle.PicklingError: Can't pickle <function <lambda> at 0x7fbbc0464e18>: 
    attribute lookup <lambda> on __main__ failed
    

    我们可以查找 很好-因为它知道自己的名字:

    >>> pickle.loads(pickle.dumps(foo))
    <function foo at 0x7fbbbee79268>
    

    lambda没有注释和docstring

    基本上,lambda没有文档记录。让我们重写 为了更好地记录:

    def foo() -> int:
        """a nullary function, returns 0 every time"""
        return 0
    

    现在,foo有文档:

    >>> foo.__annotations__
    {'return': <class 'int'>}
    >>> help(foo)
    Help on function foo in module __main__:
    
    foo() -> int
        a nullary function, returns 0 every time
    

    然而,我们没有相同的机制来向lambda提供相同的信息:

    >>> help(l)
    Help on function <lambda> in module __main__:
    
    <lambda> lambda (...)
    

    但我们可以破解它们:

    >>> l.__doc__ = 'nullary -> 0'
    >>> l.__annotations__ = {'return': int}
    >>> help(l)
    Help on function <lambda> in module __main__:
    
    <lambda> lambda ) -> in
        nullary -> 0
    

    但是,可能有一些错误把帮助的输出搞得一团糟。

    lambda只能返回表达式

    lambda不能返回复杂语句,只能返回表达式。

    >>> lambda: if True: 0
      File "<stdin>", line 1
        lambda: if True: 0
                 ^
    SyntaxError: invalid syntax
    

    诚然,表达是相当复杂的,如果你尝试 非常 很难用lambda完成相同的工作,但是增加的复杂性对编写清晰的代码更不利。

    为了清晰和可维护性,我们使用了python。过度使用羊羔肉可能会适得其反。

    这个 只有 lambdas的upside:可以在单个表达式中创建

    这是唯一可能的优势。由于可以使用表达式创建lambda,因此可以在函数调用内部创建它。

    在函数调用中创建函数可以避免(便宜的)名称查找,而不是在其他地方创建。

    但是,由于严格评估了python,因此除了避免名称查找之外,没有其他性能方面的改进。

    对于非常简单的表达式,我可以选择lambda。

    我还倾向于在做交互式Python时使用lambda,以避免在一个需要时使用多行。当我想在调用时将参数传递给构造函数时,我使用以下类型的代码格式 timeit.repeat :

    import timeit
    
    def return_nullary_lambda(return_value=0):
        return lambda: return_value
    
    def return_nullary_function(return_value=0):
        def nullary_fn():
            return return_value
        return nullary_fn
    

    现在:

    >>> min(timeit.repeat(lambda: return_nullary_lambda(1)))
    0.24312214995734394
    >>> min(timeit.repeat(lambda: return_nullary_function(1)))
    0.24894469301216304
    

    我相信上述细微的时间差异可以归因于 return_nullary_function -注意它是 非常 可以忽略不计的。

    结论

    lambda非常适合非正式的情况,在这种情况下,您希望将代码行最小化,从而有利于生成一个单点。

    对于更正式的情况来说,lambda是不好的,因为在这种情况下,您需要清晰的代码编辑器,这些编辑器稍后会出现,特别是在那些代码非常重要的情况下。

    我们知道我们应该给我们的物品起个好名字。当物体 名字?

    出于所有这些原因,我更喜欢使用 DEF 而不是 lambda .

        12
  •  1
  •   too much php    16 年前

    如果您只想将lambda分配给本地作用域中的变量,那么您也可以使用def,因为它更可读,将来更容易扩展:

    fun = lambda a, b: a ** b # a pointless use of lambda
    map(fun, someList)
    

    def fun(a, b): return a ** b # more readable
    map(fun, someList)
    
        13
  •  0
  •   Bite code    16 年前

    我同意Nosklo。顺便说一下,即使有 使用一次,扔掉 函数,大多数时候您只想使用来自操作员模块的某些内容。

    例如:

    您有一个带有这个签名的函数:myfunction(数据,回调函数)。

    您希望传递一个添加2个元素的函数。

    使用lambda:

    myFunction(data, (lambda x, y : x + y))
    

    蟒蛇之道:

    import operator
    myFunction(data, operator.add)
    

    当然,这是一个简单的例子,但是操作员模块提供了很多东西,包括list和dict的items setters/getter。真的很酷。

        14
  •  0
  •   bhargav patel    7 年前
    • 计算时间。
    • 函数没有名称。
    • 实现一个功能和多个使用功能。

    考虑到一个简单的例子,

    # CREATE ONE FUNCTION AND USE IT TO PERFORM MANY OPERATIONS ON SAME TYPE OF DATA STRUCTURE.
    def variousUse(a,b=lambda x:x[0]):
        return [b(i) for i in a]
    
    dummyList = [(0,1,2,3),(4,5,6,7),(78,45,23,43)]
    variousUse(dummyList)                           # extract first element
    variousUse(dummyList,lambda x:[x[0],x[2],x[3]]) # extract specific indexed element
    variousUse(dummyList,lambda x:x[0]+x[2])        # add specific elements
    variousUse(dummyList,lambda x:x[0]*x[2])        # multiply specific elements
    
        15
  •  -1
  •   Nuss    16 年前

    lambda可用于生成新函数:

    def somefunc(x): return lambda y: x+y
    f = somefunc(10)
    f(2)
    >>> 12
    f(4)
    >>> 14
    
        16
  •  -1
  •   Ali Rasim Kocal    7 年前

    一个主要的区别是你不能使用 def 函数内联,我认为这是 lambda 功能。例如,对对象列表排序时:

    my_list.sort(key=lambda o: o.x)
    

    因此,我建议将lambda的使用保留在这种琐碎的操作中,这也不会真正受益于通过命名函数提供的自动文档。