'lambda'关键字的缩写替代方案?

时间:2017-08-19 00:44:53

标签: python lambda programming-languages keyword

背景

Python是关于简单易读的代码。它比版本更好,我是一个巨大的粉丝!但是,每次我必须定义lambda时输入l a m b d a并不好玩(您可能不同意)。 问题是,这些6个字符l a m b d a使我的声明更长,因为我在{{}}内嵌了几个lambdas { {1}} s和map s(我没有嵌套超过2或3,因为它消除了python的可读性 - 这里没有参数!)

实际问题(在评论中):

filter

我很乐意添加这样的导入:

# How to rename/alias a keyword to a nicer one? 
lines = map(lmbd 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)

3 个答案:

答案 0 :(得分:11)

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

所以而不是:

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并不有用!有很多地方lambda非常方便。考虑key argument for sorted(同样适用于minmax)或functools.reduce(但最好远离该功能,大部分时间都是正常for - 循环更具可读性)或itertools需要谓词函数:itertools.accumulateitertools.dropwhileitertools.groupbyitertools.takewhile。仅举一些lambda可能有用的例子,可能还有很多其他地方。

答案 1 :(得分:4)

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

from operator import methodcaller as mc

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

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

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

答案 2 :(得分:1)

作为一个除了调试目的外,从不在其代码中使用lambda的人,我可以提出几种替代方案。

我不会谈论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一起使用要比maplambdaanother 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来重写。如果您了解更多信息,请在评论中写下它,我会将其添加到我的答案中。