Python Tip 3: Python lambda表达式

Python中的Lambda表达式很可能让人迷惑,我们还是从一段官方解释文字开始:

Small anonymous functions can be created with the lambda keyword. This function returns the sum of its two arguments: lambda a, b: a+b. Lambda functions can be used wherever function objects are required. They are syntactically restricted to a single expression. Semantically, they are just syntactic sugar for a normal function definition.

理解lambda表达式的本质需要回答两个问题:为什么需要lambda?lambda的好处在哪里?

首先为什么需要lambda? 让我们看几个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
>>> add_one = lambda x: x + 1
>>> add_one(1)
2
>>> add_one(2)
3
>>> add_one(100)
101
>>> def add_one_fun(x):
...     return x + 1
... 
>>> add_one_fun(1)
2
>>> add_one_fun(100)
101
>>> x_sum_y = lambda x, y: x + y
>>> x_sum_y(1, 1)
2
>>> x_sum_y(1, 10)
11
>>> def sum_fun(x, y):
...     return x + y
... 
>>> sum_fun(1, 1)
2
>>> sum_fun(1, 2)
3
>>> sum_fun(1, 10)
11

很遗憾,lambda并非必须,每个lambda都可以用函数替代。那么我们为什么需要lambda表达式?或者lambda表达式有什么优点?其实,官方文档里已经说得很明确了:首先,它是一个小的匿名函数,可以用在任何需要函数对象的地方,它的语法被严格限制在一行表达式(并且不应该太复杂);从语义上来讲,它们只是正常的函数定义的语法糖。

Python不是一个纯函数式编程语言,但是引入了sorted, map, reduce, filter等函数式编程的一些特性, 可以将函数作为参数传给这些函数,这里,lambda表达式就派上用场了。我们熟悉的大概是sorted函数了:

1
2
3
4
5
>>> test_list = [('two', 2), ('three', 3), ('one', 1), ('five', 5), ('four', 4)]
>>> sorted(test_list, key=lambda x: x[1])
[('one', 1), ('two', 2), ('three', 3), ('four', 4), ('five', 5)]
>>> sorted(test_list, key=lambda x: x[0])
[('five', 5), ('four', 4), ('one', 1), ('three', 3), ('two', 2)]

map() 接收一个 function 和一个 iterable 作为参数,该函数会返回一个迭代器,该迭代器将 function 应用于 iterable 的所有元素,并产生结果。下述示例,对列表中所有元素求平方和立方:

1
2
3
4
5
6
7
>>> test_list = [2, 3, 5, 7, 11, 13, 19, 17]
>>> map_list = list(map(lambda x: x ** 2, test_list))
>>> map_list
[4, 9, 25, 49, 121, 169, 361, 289]
>>> map_list = list(map(lambda x: x ** 3, test_list))
>>> map_list
[8, 27, 125, 343, 1331, 2197, 6859, 4913]

reduce() 函数会对参数序列中元素进行累积。函数将一个数据集合(链表,元组等)中的所有数据进行下列操作:用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果。注意在python3中reduce已经从全局函数中移除,需要从functools中import,我们来看一个1到100整数求和的例子:

1
2
3
4
5
6
>>> test_list = list(range(1, 101))
>>> test_list
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
>>> from functools import reduce
>>> reduce(lambda x, y: x + y, test_list)
5050

filter() 提供了一种优雅的方法来过滤 iterable(可迭代对象,例如:列表)中的所有元素,过滤条件是 function 返回 True:

1
2
3
4
5
6
7
>>> test_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
>>> filter_list = list(filter(lambda x: x % 3 == 0, test_list))
>>> filter_list
[3, 6, 9, 12, 15]
>>> filter_list = list(filter(lambda x: x % 5 == 0, test_list))
>>> filter_list
[5, 10, 15]

注:原创文章,转载请注明出处及保留链接“Python时代”:http://www.pythonage.com/

本文链接地址:Python Tip 3: Python lambda表达式 http://www.pythonage.com/?p=47

Python Tip 2: Python *args **kwargs 用法

关于Python中 *args 和 **kwargs的用法,网上有很多介绍文章,还是先看一段官方的解释:

When a final formal parameter of the form **name is present, it receives a dictionary (see Mapping Types — dict) containing all keyword arguments except for those corresponding to a formal parameter. This may be combined with a formal parameter of the form *name (described in the next subsection) which receives a tuple containing the positional arguments beyond the formal parameter list. (*name must occur before **name.)

首先需要了解Python函数传递参数的方式有两种:

位置参数(positional argument)
关键词参数(keyword argument)

然后再来看*args(*name)与**kwargs(**name)的区别,两者都是python中的可变参数:

*args表示任何多个无名参数,它本质是一个tuple;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
>>> def args_one(narg, *args):
...     print("Normal arg:", narg)
...     for arg in args:
...         print("other args:", arg)
... 
>>> args_one(1, 2, 3, 4, 5)
Normal arg: 1
other args: 2
other args: 3
other args: 4
other args: 5
>>> args_one('a', 'b', 'c', 'd', 'e')
Normal arg: a
other args: b
other args: c
other args: d
other args: e
>>> tuple_args = (1, 2, 3, 4, 5)
>>> args_one(1, tuple_args)
Normal arg: 1
other args: (1, 2, 3, 4, 5)
>>> args_one(1, *tuple_args)
Normal arg: 1
other args: 1
other args: 2
other args: 3
other args: 4
other args: 5

**kwargs表示关键字参数,它本质上是一个dict:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
>>> def args_two(narg, **kwargs):
...     print("Normal arg:", narg)
...     for key, value in kwargs.items():
...         print("other keyword arg: %s: %s" % (key, value))
... 
>>> args_two(narg=1, karg1="one", karg2="two")
Normal arg: 1
other keyword arg: karg1: one
other keyword arg: karg2: two
>>> key_args = {"a": "A", "b": "B", "c": "C"}
# 以下两个是错误的调用示范
>>> args_two('a', key_args)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: args_two() takes 1 positional argument but 2 were given
>>> args_two('a', *key_args)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: args_two() takes 1 positional argument but 4 were given
# 正确做法
>>> args_two('a', **key_args)
Normal arg: a
other keyword arg: a: A
other keyword arg: b: B
other keyword arg: c: C

同时使用*args和**kwargs时,必须*args参数列要在**kwargs前, 让我们最后来看一个综合例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
>>> def args_fun(normal, *args, **kwargs):
...     print("narmal=", normal)
...     print("*args=", args)
...     print("**args=", kwargs)
... 
>>> args_fun(1)
narmal= 1
*args= ()
**args= {}
>>> args_fun(1, 2)
narmal= 1
*args= (2,)
**args= {}
>>> args_fun(1, 2, 3)
narmal= 1
*args= (2, 3)
**args= {}
>>> args_fun(1, a=1, b=2, c=3)
narmal= 1
*args= ()
**args= {'a': 1, 'b': 2, 'c': 3}
>>> args_fun(1, 2, 3, a=1, b=2, c=3)
narmal= 1
*args= (2, 3)
**args= {'a': 1, 'b': 2, 'c': 3}
>>> args_fun(1, a=1, b=2, c=3, 2, 3)
  File "<stdin>", line 1
SyntaxError: positional argument follows keyword argument

注:原创文章,转载请注明出处及保留链接“Python时代”:http://www.pythonage.com/

本文链接地址:Python Tip 2: python *args **kwargs 用法 http://www.pythonage.com/?p=15

The Zen of Python

The Zen of Python

by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!