【问题标题】:Shorter alternative for 'lambda' keyword?'lambda' 关键字的更短的替代方案?
【发布时间】:2018-01-27 17:30:21
【问题描述】:

背景:

Python 是关于简单和可读的代码。它比版本更好,我是一个超级粉丝!然而,每次我必须定义一个 lambda 时,输入lambda 并不好玩(你可能不同意)。 问题是,这 6 个字符 l a m b d a 使我的语句更长,特别是如果我在 maps 和 filters 中嵌套了几个 lambdas。 我不会嵌套超过 2 或 3 个,因为它带走了 python 的可读性,即使输入 l a m b d a 感觉太冗长了。

实际问题(在 cmets 中):

# How to rename/alias a keyword to a nicer one? 
lines = map(lmd x: x.strip(), sys.stdin)

# OR, better yet, how to define my own operator like -> in python?
lines = map(x -> x.strip(), sys.stdin)
# Or may be :: operator is pythonic
lines = map(x :: x.strip(), sys.stdin)

# INSTEAD of this ugly one. Taking out this is my goal!
lines = map(lambda x: x.strip(), sys.stdin)

我很高兴像这样添加导入:

from myfuture import lmd_as_lambda
# OR
from myfuture import lambda_operator

【问题讨论】:

  • “当我在映射和过滤器中嵌套几个 lambda 时” 改用生成器表达式?
  • 编写函数而不是 lambda。它们更容易调试。 Lambda 很有用,但应该适度使用。如果输入 lambda 太麻烦,也许这表明你使用它们太多了。
  • 传递str.strip有什么问题?
  • @IgnacioVazquez-Abrams 它不等同于lambda x: x.strip()。那里可能有 bytes (python-3.x) 或 unicodes (python-2.x) - 或者其他完全可以被剥离的东西。 sys.stdin 不太可能(不可能?),但在更一般的情况下可能会出现问题。
  • 除了避免使用它之外,您无法缩写 Python 关键字。与 C/C++ 不同,没有扩展宏的预处理器步骤。

标签: python lambda programming-languages keyword


【解决方案1】:

好消息是:你根本不需要使用mapfilter,你可以使用generator expressions(懒惰)或list comprehensions(渴望),从而完全避免lambdas .

所以而不是:

lines = map(lambda x: x.strip(), sys.stdin)

只需使用:

# You can use either of those in Python 2 and 3, but map has changed between
# Python 2 and Python 3 so I'll present both equivalents:
lines = (x.strip() for x in sys.stdin)  # generator expression (Python 3 map equivalent)
lines = [x.strip() for x in sys.stdin]  # list comprehension   (Python 2 map equivalent)

如果您使用推导式,它可能也会更快。在mapfilter 中使用时,实际上很少有函数会更快 - 而使用lambda 则有更多的反模式(而且速度很慢)。


该问题仅包含map 的示例,但您也可以替换filter。比如你想filter出奇数:

filter(lambda x: x%2==0, whatever)

您可以改用条件推导:

(x for x in whatever if x%2==0)
[x for x in whatever if x%2==0]

您甚至可以将mapfilter 组合成一个理解:

(x*2 for x in whatever if x%2==0)

想想mapfilter 会是什么样子:

map(lambda x: x*2, filter(lambda x: x%2==0, whatever))

注意:这并不意味着lambda 没有用!有很多地方lambdas 非常方便。考虑key argument for sorted(同样适用于minmax)或functools.reduce(但最好远离该函数,大多数情况下,普通for循环更具可读性)或itertools需要一个谓词函数:itertools.accumulateitertools.dropwhileitertools.groupbyitertools.takewhile。仅举几个 lambda 可能有用的例子,可能还有很多其他地方。

【讨论】:

  • 谢谢。今天我学习了Generator Expressions,它解决了我的需求。我会在短时间内接受这个答案。等着看是否有人对::-> 运营商有想法)
  • Python 2 也支持generator expressions,所以你所说的关于它们和 Python 3 的说法是不正确的。
  • @martineau 我从来没有说过 python-2.x 不支持生成器表达式。我所说的(和意思)是 map 的等价物是 python-2.x 中的列表理解和 python-3.x 中的生成器表达式
  • 您似乎是在说生成器表达式在 Python 2 中也不等效。
  • @martineau 它不等同于 python-2.x 上的map。但这并不意味着在 python-2.x 中没有生成器表达式。我已经更新了答案并澄清了一点。如果仍然模棱两可,请告诉我。
【解决方案2】:

为了回答您的具体问题,operator 模块提供了几个函数,旨在替换 lambda 表达式的特定用途。在这里,您可以使用methodcaller 函数来创建调用对象上给定方法的函数。

from operator import methodcaller as mc

lines = map(mc('strip'), sys.stdin)

但是,列表推导往往比 map 的许多(如果不是大多数)用法更受欢迎。

lines = [x.strip() for x in sys.stdin]

【讨论】:

  • 是的,operator 模块对于map 等来说非常方便。这种情况下也可以考虑直接使用str.strip(如果只有纯strs)。跨度>
  • 好点。 methodcaller 在您需要依赖鸭子类型时很有用(即,我不知道 x 会有什么类型,但我知道它需要有一个 strip 方法)或者当您需要传递其他参数时到方法。否则,您可以使用未绑定的方法作为函数参数。
  • 感谢您的建议。 1. 方法调用者可能并不总是可行的,例如,如果我的 lambda 获取一个元组并交换位置并返回一个新元组。 2. [x.strip() for x in sys.stdin] 这很优雅,但我的数据流是来自生成器的懒惰yielded。我刚刚从@MSeifert 的回答中了解到,当我将[]() 交换时,python 3 支持惰性评估。
  • @ThammeGowda 澄清一下:python-2.x 和 3.x 都支持生成器表达式(惰性求值)。我的意思是 mapfilter 在 python-2.x 中是渴望的,但在 python-3.x 中是懒惰的。
  • @MSeifert 谢谢。是的,刚刚查了一下,它在 2.4 中被接受为 PEP-289 python.org/dev/peps/pep-0289
【解决方案3】:

作为一个在他的代码中除了调试目的之外从不使用 lambdas 的人,我可以建议几个替代方案。

我不会谈论 defining your own syntax in an editor(虽然你不能在纯 Python 中定义运算符:Python: defining my own operators?),但只会谈论内置的东西。

  1. built-in types的方法
    比较以下内容:
    words = ['cat', 'dog', 'shark']
    result_1 = map(lambda x: x.upper(), words)
    result_2 = (x.upper() for x in words)
    result_3 = map(str.upper, words)
    # ['CAT', 'DOG', 'SHARK']
    
    mapstr.upper 一起使用比maplambda 以及another answer 中提出的生成器表达式都要短。
    您可以在docs 中找到许多其他针对不同类型的方法,例如intfloatstrbytes 等,您可以以相同的方式使用它们。例如,检查数字是否为整数:
    numbers = [1.0, 1.5, 2.0, 2.5]
    result_1 = map(lambda x: x.is_integer(), numbers)
    result_2 = (x.is_integer() for x in numbers)
    result_3 = map(float.is_integer, numbers)
    # [True, False, True, False]
    
  2. Class methods:
    以类似的方式,您可以将map 与类方法一起使用:

    class Circle:
        def __init__(self, radius):
            self.radius = radius
        def area(self):
            return 3.14 * self.radius ** 2
    
    circles = [Circle(2), Circle(10)]
    result_1 = map(lambda x: x.area(), circles)
    result_2 = (x.area() for x in circles)
    result_3 = map(Circle.area, circles)
    # [12.56, 314.0]
    
  3. operator模块:

    • itemgetter:
      当你想通过索引选择元素时使用这个:

      from operator import itemgetter
      
      numbers = [[0, 1, 2, 3],
                 [4, 5, 6, 7],
                 [8, 9, 0, 1]]
      result_1 = map(lambda x: x[0], numbers)
      result_2 = (x[0] for x in numbers)
      result_3 = map(itemgetter(0), numbers)
      # [0, 4, 8]
      

      虽然它比给定示例中的生成器表达式长,但当您想一次选择多个元素时,它实际上会更短:

      result_1 = map(lambda x: (x[0], x[2], x[3]), numbers)
      result_2 = ((x[0], x[2], x[3]) for x in numbers)
      result_3 = map(itemgetter(0, 2, 3), numbers)
      # [(0, 2, 3), (4, 6, 7), (8, 0, 1)]
      

      您也可以将itemgetter 与字典一起使用:

      data = [{'time': 0, 'temperature': 290, 'pressure': 1.01},
              {'time': 10, 'temperature': 295, 'pressure': 1.04},
              {'time': 20, 'temperature': 300, 'pressure': 1.07}]
      
      result_1 = map(lambda x: (x['time'], x['pressure']), data)
      result_2 = ((x['time'], x['pressure']) for x in data)
      result_3 = map(itemgetter('time', 'pressure'), data)
      # [(0, 1.01), (10, 1.04), (20, 1.07)]
      
    • attrgetter
      这个是用来获取对象属性的:

      from collections import namedtuple
      from operator import attrgetter
      
      Person = namedtuple('Person', ['name', 'surname', 'age', 'car'])
      people = [Person(name='John', surname='Smith', age=40, car='Tesla'), 
                Person(name='Mike', surname='Smith', age=50, car=None)]
      result_1 = map(lambda x: (x.name, x.age, x.car), people)
      result_2 = ((x.name, x.age, x.car) for x in people)
      result_3 = map(attrgetter('name', 'age', 'car'), people)
      # [('John', 40, 'Tesla'), ('Mike', 50, None)]
      

      它比生成器表达式版本长,所以我把它留在这里只是为了完整。当然,您可以将attrgetter 导入为get,它会更短,但没有人真正这样做。不过,使用attrgetter 有一个优势,您可以将其作为单独的可调用对象取出,可以多次使用(与lambda 相同):

      get_features = attrgetter('name', 'age', 'car')
      group_1_features = map(get_features, people)
      group_2_features = map(get_features, other_people)
      ...
      

      另一个值得一提的替代方法是使用 fget 属性方法:

      result = map(Person.age.fget, people)
      

      不过,我从未见过有人使用它,因此请准备好向那些在您使用它时会阅读您的代码的人进行解释。

    • contains:
      用于检查一个元素是否存在于另一个对象/容器中:

      from functools import partial
      from operator import contains
      
      fruits = {'apple', 'peach', 'orange'}
      objects = ['apple', 'table', 'orange']
      result_1 = map(lambda x: x in fruits, objects)
      result_2 = (x in fruits for x in objects)
      is_fruit = partial(contains, fruits)
      result_3 = map(is_fruit, objects)
      # [True, False, True]
      

      不过,这有一个缺点,就是创建了一个额外的partial 对象。另一种写法是使用__contains__ 方法:

      result = map(fruits.__contains__, objects)
      

      但有些人认为使用 dunder 方法是一种不好的做法,因为这些方法仅供私人使用。

    • 数学运算:
      例如,如果您想对数对求和,可以使用operator.add

      from itertools import starmap
      from operator import add
      
      pairs = [(1, 2), (4, 3), (1, 10), (2, 5)]
      result_1 = map(lambda x: x[0] + x[1], pairs)
      result_2 = (x + y for x, y in pairs)
      result_3 = starmap(add, pairs)
      # [3, 7, 11, 7]
      

      如果您对两个额外的导入没问题,那么这是最短的选择。请注意,我们在这里使用itertools.starmap,因为我们需要在将数字元组提供给add(a, b) 函数之前对其进行解包。


我想我涵盖了我经常遇到的大多数情况,这些情况可以在没有lambda 的情况下重写。如果您知道更多,请写在评论中,我会将其添加到我的答案中。

【讨论】:

  • 只是想指出,使用未绑定的方法作为参数比lambda 或理解要严格得多,因为您现在仅限于一种类型(或者在自定义类的情况下)它鸭式正确)。例如str.upper 仅适用于字符串,如果列表中有其他类型(例如字节或混合类型列表),它将不起作用。
猜你喜欢
  • 2019-10-20
  • 1970-01-01
  • 1970-01-01
  • 2014-10-25
  • 1970-01-01
  • 1970-01-01
  • 2015-01-21
  • 2023-04-05
相关资源
最近更新 更多