Python 进阶系列1:lambda函数与高阶函数

.

Python作为一门流行的编程语言,广受程序员与数据科学家的喜爱。如今的Python拥有丰富的第三方库和良好的生态发展,使其能够在数据科学、人工智能、web等各领域获得广泛的支持。Python进阶系列旨在分享一些Python的高级特性,如Lambda表达式、元类(metaclass)的使用、类(class)和类型(type)的统一、经典类与新式类区别与应用等。

作者:DjangoPeng

lambda函数

在计算机编程中,匿名函数(anonymous function)是指一类无需定义标识符(函数名)的函数子程序,普遍存在于多种编程语言中。1958年LISP首先采用匿名函数,自此之后,越来越多编程语言陆续采用,如C++、Java、Python。

lambda作为一个关键字,用来实现Python的匿名函数,与def定义的普通函数相比较而言,lambda是单一的表达式,而不是代码块(block)。我们只能在lambda函数中封装有限的逻辑(通常就是一个表达式),这样做的目的是为了引导大家用lambda实现功能简单的函数,而复杂的逻辑仍然交由def处理。

定义

Python支持在运行时动态创建Lambda函数(匿名函数)。

语法:

1
lambda arg1, arg2,...,argN : an expression with args

以”:”作为分割,前为函数入参,后为整个函数的表达式,计算结果就作为函数的返回结果。

定义求一个数的平方的函数

lambda函数

1
>>> lambda x : x**2

def函数

1
2
>>> def pow(x):
... return x**2

通过对比,可以发现lambda函数的语法更加简练,无需显示”return”,而是直接返回表达式计算结果。

调用方式

方式1:lambda函数作为一个函数对象,同样可以将其赋值给变量,然后就像普通函数一样通过func()形式进行调用:

1
2
3
>>> pow = lambda x, x**2
>>> result = pow(2)
4

方式2:直接在lambda函数后加上一对小括号进行调用:

1
2
>>>(lambda x : x**2)(2)
4

高阶函数

当一个函数接受另一个函数对象作为入参时,这种函数就称为高阶函数(Higher-order function)。

定义

定义一个高阶函数用于求两个数的和,同时接受一个函数对这两个数进行预处理,如:绝对值、开方、平方等。

1
2
>>> def myAdd(a, b, f):
... return f(a) + f(b)

其中a,b是用于求和的两个参数,f是对其进行预处理的函数。不妨先令f=abs,则有:

1
2
>>> myAdd(1, -1, abs)
2

我们之前定义了求平方的函数pow:

1
2
3
4
>>> def pow(x):
... return x**2
>>> myAdd(2, -2, pow)
8

正如之前所言,pow函数只有一个简单的表达式,完全可以由lambda函数代替:

1
2
>>> myAdd(2, -2, lambda x : x**2)
8

实例

下面实例中的map、reduce、filter都是Python内置的高阶函数,

1
2
3
4
5
6
7
8
9
# 求[0,1,2,3,4,5,6,7,8,9]各自的平方
>>> map(lambda x : x**2, xrange(10))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# 求0~99的和
>>> reduce(lambda x, y: x + y, xrange(100))
4950
# 求0~10中的偶数
>>> filter(lambda x: x % 2==0, xrange(10))
[0, 2, 4, 6, 8]

如果用def定义普通函数来完成上面的工作,以求和为例,则显得过于冗长:

1
2
3
4
5
>>> def sum(a,b):
... return a + b
...
>>> reduce(sum, xrange(100))
4950

数字字符串转换

输入一个字符串数字,将其转换为整型数字,通过lambda将其封装成str2int函数:

1
2
3
4
5
6
>>> def str2int(s):
... return reduce(lambda x, y: int(x)*10 + int(y), s)
...
>>> num = str2int('12345')
>>> print(num)
12345

嵌套排序问题

当实现各种特定的排序需求时,如dict按value排序、list嵌套dict排序等,如果结合sorted()函数和lambda来处理,往往能达到事半功倍的效果。

sorted()函数原型:

1
sorted(iterable[,key][,reverse])

iterable是需要排序的变量,key是指定排序的元素,reverse指定是否逆序。

问题1:dict按key排序

1
2
3
4
5
>>> dict = {'a':3, 'b': 1, 'c':2}
>>> sorted(dict)
['a', 'b', 'c']
>>> sorted(dict, reverse=True)
['c', 'b', 'a']

问题2:dict按value排序

1
2
3
4
5
>>> dict = {'a':3, 'b': 1, 'c':2}
>>> sorted(dict, key=lambda k: dict[k])
['b', 'c', 'a']
>>> sorted(dict, key=lambda k: dict[k], reverse=True)
['a', 'c', 'b']

问题3:list嵌套list排序

1
2
3
4
5
6
7
>>> list = [[7, 4, 1], [2, 8, 5], [6, 9, 3]]
>>> sorted(list, key=lambda k: k[0]) #按各列表第0个升序
[[2, 8, 5], [6, 9, 3], [7, 4, 1]]
>>> sorted(list, key=lambda k: k[0], reverse=True) #按各列表第0个降序
[[7, 4, 1], [6, 9, 3], [2, 8, 5]]
>>> sorted(list, key=lambda k: k[1]) #按各列表第1个升序
[[7, 4, 1], [2, 8, 5], [6, 9, 3]]

问题4:list嵌套dict排序

1
2
3
4
5
6
7
8
9
10
11
>>> list = [
... {'x': 3, 'y': 2, 'z': 1},
... {'x': 2, 'y': 1, 'z': 3},
... {'x': 1, 'y': 3, 'z': 2},
... ]
>>> sorted(list, key=lambda k: k['x']) #按dict中x的value升序
[{'y': 3, 'x': 1, 'z': 2}, {'y': 1, 'x': 2, 'z': 3}, {'y': 2, 'x': 3, 'z': 1}]
>>> sorted(lis, key=lambda k: k['x'], reverse=True) #按dict中x的value降序
[{'y': 2, 'x': 3, 'z': 1}, {'y': 1, 'x': 2, 'z': 3}, {'y': 3, 'x': 1, 'z': 2}]
>>> sorted(list, key=lambda k: k['y']) #按dict中y的value升序
[{'y': 1, 'x': 2, 'z': 3}, {'y': 2, 'x': 3, 'z': 1}, {'y': 3, 'x': 1, 'z': 2}]

问题5:dict嵌套list排序

1
2
3
4
5
6
7
8
9
10
11
>>> dict = {
... 'a': [1, 2, 3],
... 'b': [2, 1, 3],
... 'c': [3, 1, 2],
... }
>>> sorted(dict, key=lambda k: dict[k][0])
['a', 'b', 'c']
>>> sorted(dict, key=lambda k: dict[k][0], reverse=True)
['c', 'b', 'a']
>>> sorted(dict, key=lambda k: dict[k][1])
['c', 'b', 'a']

总结

在实现简单功能的函数时,为了提升代码可读性,减少函数定义,可以尝试使用lambda编写匿名函数。其好处是显而易见的,当其与常用的高阶函数如map、reduce、fileter、sorted等结合时,能够产生事半功倍的效果。

升级系统glibc 指导文档

注意:升级系统的glibc 库是一件风险极高的操作,升级失败会导致系统崩溃和数据丢失。请再次确认是否有其他的代替升级GLIBC的解决方案。

本文档以RHEL 6.4 (glibc 2.12)为例,将系统GLIBC升级至2.14。

1.查看系统glibc版本

1
2
$ ll /lib64/libc.so.6
lrwxrwxrwx. 1 root root 12 Oct 28 19:18 /lib64/libc.so.6 -> libc-2.12.so

2.编译glibc-2.14

1
2
3
4
5
6
7
8
9
$ tar xvf glibc-2.14.tar.gz
$ tar xvf glibc-ports-2.14.tar.gz
$ mv glibc-ports-2.14 glibc-2.14/ports
$ mkdir glibc-2.14-build
$ cd glibc-2.14-build
# Configure glibc 2.14
$ ../glibc-2.14/configure --prefix=/usr --disable-profile --enable-add-ons --with-headers=/usr/include --with-binutils=/usr/bin
# Build
$ make

3.安装glibc-2.14

1
2
# 根据系统的环境不同,安装过程中可能出现错误
$ make install

4.检查是否升级成功

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
# 如果出现错误,首先查看/lib64下是否安装libc-2.14
$ ll /lib64/libc*
-rwxr-xr-x. 1 root root 1916568 Nov 21 2012 libc-2.12.so
-rwxr-xr-x 1 root root 9643763 Dec 23 17:30 libc-2.14.so
lrwxrwxrwx. 1 root root 18 Aug 15 18:00 libcap-ng.so.0 -> libcap-ng.so.0.0.0
-rwxr-xr-x. 1 root root 18672 Nov 5 2010 libcap-ng.so.0.0.0
lrwxrwxrwx. 1 root root 14 Aug 15 17:59 libcap.so.2 -> libcap.so.2.16
-rwxr-xr-x. 1 root root 16600 Aug 23 2011 libcap.so.2.16
lrwxrwxrwx. 1 root root 19 Aug 15 18:02 libcgroup.so.1 -> libcgroup.so.1.0.37
-rwxr-xr-x. 1 root root 80568 Dec 20 2012 libcgroup.so.1.0.37
-rwxr-xr-x. 1 root root 197064 Nov 21 2012 libcidn-2.12.so
lrwxrwxrwx. 1 root root 15 Aug 15 17:59 libcidn.so.1 -> libcidn-2.12.so
lrwxrwxrwx. 1 root root 17 Aug 15 17:59 libcom_err.so.2 -> libcom_err.so.2.1
-rwxr-xr-x. 1 root root 14664 Oct 13 2012 libcom_err.so.2.1
-rwxr-xr-x. 1 root root 40400 Nov 21 2012 libcrypt-2.12.so
lrwxrwxrwx. 1 root root 22 Aug 15 18:02 libcryptsetup.so.1 -> libcryptsetup.so.1.1.0
-rwxr-xr-x. 1 root root 94312 Feb 29 2012 libcryptsetup.so.1.1.0
lrwxrwxrwx. 1 root root 16 Aug 15 17:59 libcrypt.so.1 -> libcrypt-2.12.so
lrwxrwxrwx 1 root root 19 Dec 23 17:39 libc.so.6 -> /lib64/libc-2.14.so
# 检查软链,编译成功会在glibc-2.14-build/libc.so.6生成软链
$ ll libc.so.6
lrwxrwxrwx 1 root root 7 Dec 23 17:24 libc.so.6 -> libc.so
# 查看系统glibc版本
$ strings libc.so | grep GLIBC
GLIBC_2.2.5
GLIBC_2.2.6
GLIBC_2.3
GLIBC_2.3.2
GLIBC_2.3.3
GLIBC_2.3.4
GLIBC_2.4
GLIBC_2.5
GLIBC_2.6
GLIBC_2.7
GLIBC_2.8
GLIBC_2.9
GLIBC_2.10
GLIBC_2.11
GLIBC_2.12
GLIBC_2.13
GLIBC_2.14
GLIBC_PRIVATE
# 说明glibc-2.14安装成功

5.更新系统库

1
2
3
4
5
$ cp libc.so /lib64/libc-2.14.so
# 删除glibc-2.12的软链
$ rm -rf /lib64/libc.so.6
# 生成glibc-2.14的软链
$ LD_PRELOAD=/lib64/libc-2.14.so ln -s /lib64/libc-2.14.so /lib64/libc.so.6

6.测试glibc-2.14

1
2
3
4
5
6
7
8
9
10
11
12
# 安装tensorflow_0.10后,测试glibc版本依赖
$ python
Python 2.7.12 |Anaconda 4.2.0 (64-bit)| (default, Jul 2 2016, 17:42:40)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-1)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
Anaconda is brought to you by Continuum Analytics.
Please check out: http://continuum.io/thanks and https://anaconda.org
>>>import tensorflow as tf
>>> hello = tf.constant("Hello TensorFlow!")
>>> sess = tf.Session()
>>> sess.run(hello)
'Hello TensorFlow!'

神经网络语言模型-NNLM

作者:一个独行的程序员

Bengio 2003年的NNLM可谓是神经网络语言模型的开山之作,并且为后来的RNNLM、Word2Vec、NMT等提供了思路,为DL4NLP打下了坚实的基础。

语言模型

通俗的说,语言模型就是判断一句话像不像是人说出来的。
形式化的定义,语言模型是借由一个概率分布,试图用概率p(s)来表示字符串s作为1个句子出现的频率:

$$
p(s) = p(w_1,…w_m)
$$

Read More

神经网络模型

作者:一个独行的程序员

神经网络中决定一个模型好坏的因素就是参数W,b,而后向传播过程就是通过计算Loss相对w或b的偏导数来不断更新参数的过程。本文简要介绍神经网络模型,详细的解释了后向传播原理

神经网络模型

在机器学习和认知科学领域,人工神经网络(artificial neural network,缩写ANN),简称神经网络(neural network,缩写NN)是一种模仿生物神经网络(动物的中枢神经系统,特别是大脑)的结构和功能的数学模型或计算模型,用于对函数进行估计或近似。神经网络由大量的人工神经元联结进行计算。现代神经网络是一种非线性统计性数据建模工具。

Read More

机器学习算法-Logistic Regression

作者:一个独行的程序员

逻辑回归(Logistic Regression)是一种简单的分类模型,由于其高效而又简单的特性,在实际中取得了非常广泛的应用。本文主要关注逻辑回归的应用场景和数学原理,并对其在多分类问题上的扩展做了简要介绍。

逻辑回归

问题

在实际生活中,我们常常会遇到很多“二选一”的问题。如:

  • 预测用户是否喜欢某推荐商品
  • 判断用户性别
  • 预测球赛的获胜队

在处理以上这些二分类问题时,逻辑回归成了我们的一把利器。通常这类问题的处理步骤为:

Read More