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

Tha*_*wda 10 python lambda programming-languages keyword

背景:

Python是关于简单易读的代码.它比版本更好,我是一个巨大的粉丝!但是,l a m b d a每次我必须键入lambda时输入并不好玩(你可能不同意).问题是,l a m b d a当我在maps和filters中嵌入几个lambdas时,这6个字符使我的语句更长(我没有嵌套超过2或3,因为它消除了python的可读性 - 这里没有参数!)

实际问题(评论中):

# 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)
Run Code Online (Sandbox Code Playgroud)

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

from myfuture import lamb_as_lambda
# OR
from myfuture import lambda_operator
Run Code Online (Sandbox Code Playgroud)

MSe*_*ert 12

好消息是:你不需要使用map或根本不需要filter使用生成器表达式(懒惰)或列表推导(渴望),从而lambda完全避免使用.

所以代替:

lines = map(lambda x: x.strip(), sys.stdin)
Run Code Online (Sandbox Code Playgroud)

只需使用:

# 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)
Run Code Online (Sandbox Code Playgroud)

如果你使用理解,它可能也会更快.在使用mapfilter使用时,很少有功能实际上更快,并且使用lambda更多的反模式(和慢速).


这个问题只包含一个例子map,但您也可以替换filter.例如,如果你想filter输出奇数:

filter(lambda x: x%2==0, whatever)
Run Code Online (Sandbox Code Playgroud)

您可以使用条件理解:

(x for x in whatever if x%2==0)
[x for x in whatever if x%2==0]
Run Code Online (Sandbox Code Playgroud)

你甚至可以结合一个mapfilter一个理解:

(x*2 for x in whatever if x%2==0)
Run Code Online (Sandbox Code Playgroud)

只要考虑一下它的外观mapfilter:

map(lambda x: x*2, filter(lambda x: x%2==0, whatever))
Run Code Online (Sandbox Code Playgroud)

注意:这并不意味着lambda没用!有很多地方的lambdas非常方便.考虑key的参数sorted(并且对于minmax)或functools.reduce(但是从功能更好地保持距离,大部分的时间正常for-loop更易读)或者itertools需要谓词函数:itertools.accumulate,itertools.dropwhile,itertools.groupbyitertools.takewhile.仅举一些lambda可能有用的例子,可能还有很多其他地方.


che*_*ner 7

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

from operator import methodcaller as mc

lines = map(mc('strip'), sys.stdin)
Run Code Online (Sandbox Code Playgroud)

然而,列表推导式往往比map.

lines = [x.strip() for x in sys.stdin]
Run Code Online (Sandbox Code Playgroud)

  • 是的,`operator` 模块对于`map` 等非常方便。在这种情况下,也可以考虑直接使用`str.strip`(如果只有纯`str`s)。 (2认同)
  • @ThammeGowda 澄清一下:python-2.x 和 3.x 都支持生成器表达式(惰性求值)。我的意思是 `map` 和 `filter` 在 python-2.x 中是渴望的,但在 python-3.x 中是惰性的。 (2认同)

Geo*_*rgy 5

作为一个除了调试目的从不在他的代码中使用 lambda 表达式的人,我可以建议几种替代方法。

我不会谈论在编辑器中定义你自己的语法(你不能在纯 Python 中定义运算符:Python:定义我自己的运算符?),而只是关于内置的东西。

  1. 方法内建类型
    比较如下:
    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']
    
    Run Code Online (Sandbox Code Playgroud) 使用mapwithstr.upper另一个答案中提出的mapwithlambda和生成器表达式都短。 你可以找到很多的其他方法的文档不同的类型,例如,,,和其他人,你可以以同样的方式使用。例如,检查数字是否为整数:
    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]
    
    Run Code Online (Sandbox Code Playgroud)
  2. 类方法
    以类似的方式,您可以使用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]
    
    Run Code Online (Sandbox Code Playgroud)
  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]
      
      Run Code Online (Sandbox Code Playgroud)

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

      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)]
      
      Run Code Online (Sandbox Code Playgroud)

      您还可以使用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)]
      
      Run Code Online (Sandbox Code Playgroud)
    • 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)]
      
      Run Code Online (Sandbox Code Playgroud)

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

      get_features = attrgetter('name', 'age', 'car')
      group_1_features = map(get_features, people)
      group_2_features = map(get_features, other_people)
      ...
      
      Run Code Online (Sandbox Code Playgroud)

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

      result = map(Person.age.fget, people)
      
      Run Code Online (Sandbox Code Playgroud)

      不过,我从未见过有人使用它,所以准备好向那些会阅读你的代码的人解释一下,如果你使用它。

    • 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]
      
      Run Code Online (Sandbox Code Playgroud)

      但是,这具有创建附加partial对象的缺点。另一种写法是使用__contains__方法:

      result = map(fruits.__contains__, objects)
      
      Run Code Online (Sandbox Code Playgroud)

      但是有些人认为使用 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]
      
      Run Code Online (Sandbox Code Playgroud)

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


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