在Python中过度使用 lambda 表达式
什么是 lambda 表达式?
lambda 表达式是一种用于创建没有名称的函数的特殊语法。这些函数称为 lambda 函数。这些 lambda 函数可以有任意数量的参数,但只有一个表达式和一个隐式返回语句。 Lambda 表达式返回函数对象。例如,考虑 lambda 表达式:
lambda (arguments) : (expression)
这个 lambda 表达式定义了一个未命名的函数,它接受两个参数并返回两个参数的和。但是我们如何调用一个未命名的函数呢?上面定义的未命名的lambda函数可以调用为:
(lambda x, y: x + y)(1, 2)
代码 1:
Python3
# Python program showing a use
# lambda function
# performing a addition of three number
x1 = (lambda x, y, z: (x + y) * z)(1, 2, 3)
print(x1)
# function using a lambda function
x2 = (lambda x, y, z: (x + y) if (z == 0) else (x * y))(1, 2, 3)
print(x2)
Python3
# Python program showing
# variable is storing lambda
# expression
# assigned to a variable
sum = lambda x, y: x + y
print(type(sum))
x1 = sum(4, 7)
print(x1)
Python3
# Python program showing
# using of normal function
def Key(x):
return x%2
nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
sort = sorted(nums, key = Key)
print(sort)
Python3
# Python program showing use
# of lambda function
nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
sort_lambda = sorted(nums, key = lambda x: x%2)
print(sort_lambda)
Python3
# Python program showing a use
# of lambda function
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
# using map() function
squares = map(lambda x: x * x, nums)
print(list(squares))
# using filter() function
evens = filter(lambda x: True if (x % 2 == 0)
else False, nums)
print(list(evens))
Python3
# Python program performing
# operation using def()
def fun(x, y, z):
return x*y+z
a = 1
b = 2
c = 3
# logical jump
d = fun(a, b, c)
print(d)
Python3
# Python program performing
# operation using lambda
d = (lambda x, y, z: x*y+z)(1, 2, 3)
print(d)
Python3
def func(x):
if x == 1:
return "one"
else if x == 2:
return "two"
else if x == 3:
return "three"
else:
return "ten"
num = func(3)
print(num)
Python3
# Python program showing use
# of lambda function
num = (lambda x: "one" if x == 1 else( "two" if x == 2
else ("three" if x == 3 else "ten")))(3)
print(num)
Python3
func = lambda x, y, z: x * y + z
print(func)
def func(x, y, z): return x * y + z
print(func)
Python3
details = [{'p':100, 'r':0.01, 'n':2, 't':4},
{'p':150, 'r':0.04, 'n':1, 't':5},
{'p':120, 'r':0.05, 'n':5, 't':2}]
sorted_details = sorted(details,
key=lambda x: x['p']*((1 + x['r']/
x['n'])**(x['n']*x['t'])))
print(sorted_details)
Python3
details = [{'p':100, 'r':0.01, 'n':2, 't':4},
{'p':150, 'r':0.04, 'n':1, 't':5},
{'p':120, 'r':0.05, 'n':5, 't':2}]
def CI(det):
'''sort key: compound interest, P(1 + r/n)^(nt)'''
return det['p']*((1 + det['r']/det['n'])**(det['n']*det['t']))
sorted_details = sorted(details, key=CI)
print(sorted_details)
Python3
nums = [0, 1, 2, 3, 4, 5]
mapped = map(lambda x: x * x, nums)
filtered = filter(lambda x: x % 2, nums)
print(list(mapped))
print(list(filtered))
Python3
nums = [0, 1, 2, 3, 4, 5]
mapped = (x * x for x in nums)
filtered = (x for x in nums if x % 2 == 1)
print(list(mapped))
print(list(filtered))
输出:
9
2
尽管不鼓励这样做,但可以将 lambda 表达式返回的函数对象分配给变量。请参阅下面的示例,其中为变量 sum 分配了一个由 lambda 表达式返回的函数对象。
Python3
# Python program showing
# variable is storing lambda
# expression
# assigned to a variable
sum = lambda x, y: x + y
print(type(sum))
x1 = sum(4, 7)
print(x1)
输出:
11
lambda 表达式的常见用途:
- 由于 lambda 函数是匿名的并且不需要分配名称,因此它们通常用于调用需要函数对象作为参数的函数(或类) 。为此类函数参数定义单独的函数是没有用的,因为函数定义通常很短,并且在代码中只需要一次或两次。例如,内置函数sorted() 的关键参数。
Python3
# Python program showing
# using of normal function
def Key(x):
return x%2
nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
sort = sorted(nums, key = Key)
print(sort)
- 输出:
[0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
Python3
# Python program showing use
# of lambda function
nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
sort_lambda = sorted(nums, key = lambda x: x%2)
print(sort_lambda)
- 输出:
[0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
- Lambda 函数是内联函数,因此在需要重复函数调用以减少执行时间时使用它们。此类场景的一些示例是函数:map()、filter() 和 sorted()。例如,
Python3
# Python program showing a use
# of lambda function
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
# using map() function
squares = map(lambda x: x * x, nums)
print(list(squares))
# using filter() function
evens = filter(lambda x: True if (x % 2 == 0)
else False, nums)
print(list(evens))
lambda 函数的优缺点:
lambda 函数的优点:
- 由于是匿名的,lambda 函数可以很容易地传递而无需分配给变量。
- Lambda 函数是内联函数,因此执行速度相对较快。
- 很多时候 lambda 函数通过避免函数调用引起的逻辑跳转来使代码更具可读性。例如,阅读以下代码块。
Python3
# Python program performing
# operation using def()
def fun(x, y, z):
return x*y+z
a = 1
b = 2
c = 3
# logical jump
d = fun(a, b, c)
print(d)
- 输出:
5
Python3
# Python program performing
# operation using lambda
d = (lambda x, y, z: x*y+z)(1, 2, 3)
print(d)
- 输出:
5
lambda函数的缺点:
- Lambda 函数只能有一个表达式。
- Lambda 函数不能有文档字符串。
- 很多时候 lambda 函数使代码难以阅读。例如,请参见下面给出的代码块。
Python3
def func(x):
if x == 1:
return "one"
else if x == 2:
return "two"
else if x == 3:
return "three"
else:
return "ten"
num = func(3)
print(num)
- 输出:
three
Python3
# Python program showing use
# of lambda function
num = (lambda x: "one" if x == 1 else( "two" if x == 2
else ("three" if x == 3 else "ten")))(3)
print(num)
- 输出:
three
滥用 Lambda 表达式:
- lambda 表达式的赋值:官方Python风格指南 PEP8,强烈反对 lambda 表达式的赋值,如下例所示。
func = lambda x, y, z: x*y + z
- 相反,建议将单行函数编写为,
def func(x, y, z): return x*y + z
- 虽然第二种方法鼓励 lambda 函数是匿名的这一事实,但它对于调试期间的回溯也很有用。运行下面的代码,看看def如何使回溯变得非常有用。
Python3
func = lambda x, y, z: x * y + z
print(func)
def func(x, y, z): return x * y + z
print(func)
- 将 lambda 表达式包装在函数周围:很多时候,lambda 表达式不必要地包装在函数周围,如下所示。
nums = [-2, -1, 0, 1, 2]
sort = sorted(nums, key=lambda x: abs(x))
- 虽然上述语法绝对正确,但程序员必须明白, Python中的所有函数都可以作为函数对象传递。因此,相同的代码可以(并且应该)写成,
sort = sorted(nums, key=abs)
- 不必要地传递函数:很多时候,程序员传递只执行单个操作的函数。请参阅以下代码。
nums = [1, 2, 3, 4, 5]
summation = reduce(lambda x, y: x + y, nums)
- 上面传递的 lambda函数只执行一个操作,将两个参数相加。使用内置函数sum可以获得相同的结果,如下所示。
nums = [1, 2, 3, 4, 5]
summation = sum(nums)
- 程序员应避免将 lambda 表达式用于常见操作,因为很可能有一个内置函数提供相同的结果。
过度使用 lambda 表达式:
- 将 lambda 用于非平凡的函数:有时,简单的函数可能是不平凡的。请参阅下面的代码。
Python3
details = [{'p':100, 'r':0.01, 'n':2, 't':4},
{'p':150, 'r':0.04, 'n':1, 't':5},
{'p':120, 'r':0.05, 'n':5, 't':2}]
sorted_details = sorted(details,
key=lambda x: x['p']*((1 + x['r']/
x['n'])**(x['n']*x['t'])))
print(sorted_details)
- 输出:
[{‘n’: 2, ‘r’: 0.01, ‘t’: 4, ‘p’: 100}, {‘n’: 5, ‘r’: 0.05, ‘t’: 2, ‘p’: 120}, {‘n’: 1, ‘r’: 0.04, ‘t’: 5, ‘p’: 150}]
- 在这里,我们根据复利对字典进行排序。现在,请参阅下面编写的代码,它使用def 。
Python3
details = [{'p':100, 'r':0.01, 'n':2, 't':4},
{'p':150, 'r':0.04, 'n':1, 't':5},
{'p':120, 'r':0.05, 'n':5, 't':2}]
def CI(det):
'''sort key: compound interest, P(1 + r/n)^(nt)'''
return det['p']*((1 + det['r']/det['n'])**(det['n']*det['t']))
sorted_details = sorted(details, key=CI)
print(sorted_details)
- 输出:
[{‘n’: 2, ‘r’: 0.01, ‘t’: 4, ‘p’: 100}, {‘n’: 5, ‘r’: 0.05, ‘t’: 2, ‘p’: 120}, {‘n’: 1, ‘r’: 0.04, ‘t’: 5, ‘p’: 150}]
- 虽然这两个代码做同样的事情,但使用def的第二个代码更具可读性。这里写在 lambda 下的表达式可能很简单,但它有一个含义(复利公式)。因此,这个表达式是不平凡的,值得一个名字。对非平凡函数使用 lambda 表达式会降低代码的可读性。
- 在多行时使用 lambda 会有所帮助:如果使用多行函数使代码更具可读性,那么使用 lambda 表达式来减少一些代码行是不值得的。例如,请参见下面的代码。
people = [(‘sam’, ‘M’, 18), (‘susan’, ‘F’, 22), (‘joy’, ‘M’, 21), (‘lucy’, ‘F’, 12)]
sorted_people = sorted(people, key=lambda x: x[1])
- 另请参阅以下使用def的代码。
def Key(person):
name, sex, age = person
return sex
sorted_people = sorted(people, key=Key)
- 看看第二个代码块中的元组解包如何使其更具可读性和逻辑性。代码的可读性应该是在协作环境中工作的程序员的首要任务。
- 对 map 和 filter 使用 lambda 表达式:如图所示,Lambda 非常常与 map() 和 filter() 一起使用。
Python3
nums = [0, 1, 2, 3, 4, 5]
mapped = map(lambda x: x * x, nums)
filtered = filter(lambda x: x % 2, nums)
print(list(mapped))
print(list(filtered))
- 下面是另一个代码块,它使用生成器表达式来实现类似的结果。
Python3
nums = [0, 1, 2, 3, 4, 5]
mapped = (x * x for x in nums)
filtered = (x for x in nums if x % 2 == 1)
print(list(mapped))
print(list(filtered))
- 与 map() 和 filter() 不同,生成器表达式是Python语言的通用功能。因此生成器增强了代码的可读性。而 map() 和 filter() 则需要这些函数的先验知识。
- 高阶函数的使用:接受其他函数对象作为参数的函数称为高阶函数(即 map() 和 filter()),这在函数式编程中很常见。如上所述,lambda 表达式通常用作高阶函数的函数参数。比较下面显示的两个代码块。
使用高阶函数reduce()
nums = [1, 2, 3, 4, 5]
product = reduce(lambda x, y: x*y, nums, 1)
- 不使用高阶函数
nums = [1, 2, 3, 4, 5]
def multiply(nums):
prod = 1
for number in nums:
prod *= number
return prod
product = multiply(nums)
- 虽然第一个代码块使用较少的代码行并且不难理解,但没有函数式编程背景的程序员会发现第二个代码块的可读性很高。除非练习正确的函数式编程范式,否则不要将一个函数传递给另一个函数,因为它会降低可读性。