前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >开源图书《Python完全自学教程》7.4函数式编程

开源图书《Python完全自学教程》7.4函数式编程

作者头像
老齐
发布2022-07-06 16:05:24
2970
发布2022-07-06 16:05:24
举报
文章被收录于专栏:老齐教室

7.4 函数式编程

函数式编程(Functional Programming)是一种编程范式,虽然不是本书重点阐述的内容,但 Python 语言很早就已经采用了一些函数式编程的概念,如1994年发布的 Python 版本中就已经有了 map()reduce()filter()lambda 运算。之所以 Python 能支持函数式编程,是因为函数在 Python 中是第一类对象(参阅7.3.1节)。

本书不会深入介绍函数式编程(本节的命名显然有所夸大),如果对这种编程范式有兴趣,或者今后工作中会用到,建议自行阅读有关专业资料。下面只介绍与 Python 函数式编程有关的几个函数,这些函数在通常的程序中也较为常见。

7.4.1 lambda 函数

Python 语言中用 def 定义的函数无一例外都有函数名称(也可以用于函数式编程),而用关键词 lambda 所创建的函数是一个匿名函数,可以把它看做是一个 lambda 表达式。其语法如下:

代码语言:javascript
复制
lambda <parameter_list>: <expression>
  • lambda :关键词;
  • <parameter_list> :用逗号(英文)分割的参数(即形参);
  • : ,英文状态下的冒号,分割参数与表达式;
  • <expression> :使用参数的表达式。

例如,有这样的函数(用 def 创建):

代码语言:javascript
复制
>>> def add(x, y): return x + y
...
>>> add
<function add at 0x7fae31d44550>
>>> add(2, 3)
5

根据 lambda 表达式的语法,将函数 add() 改写为:

代码语言:javascript
复制
>>> lambda x, y: x + y
<function <lambda> at 0x7fae31d44430>

由此可知,由 lambda 关键词所创建的所谓表达式,实际上是一个函数对象,称为 lambda 函数,只不过由于它没有名字,我们不能像 add() 函数那样,使用其名称 add 来引用它。但是,既然它是对象,就可以通过赋值语句将它用变量引用。所以:

代码语言:javascript
复制
>>> add_lam = lambda x, y: x + y
>>> add_lam
<function <lambda> at 0x7fae31d44430>

现在变量 add_lam 引用了一个函数对象,如果调用该对象,与之前调用函数的方法则无异。

代码语言:javascript
复制
>>> add_lam(2, 3)
5

此外,也可以这样给 lambda 函数传实参。

代码语言:javascript
复制
>>> (lambda x, y: x + y)(2, 3)
5

显然,第一组圆括号内的就是 lambda 函数对象本身。

lambda 函数的最大特点就是以一个逻辑行代码创建了匿名函数。在某些应用中这样的代码会更简洁,应用得当可读性也很好。

例如判断 range(-5, 5) 中每个数是否大于 0 ,用 lambda 函数可以写成:

代码语言:javascript
复制
>>> [(lambda x: x>0)(n) for n in range(-5, 5)]
[False, False, False, False, False, False, True, True, True, True]

如果这样写:

代码语言:javascript
复制
>>> [x > 0 for x in range(-5, 5)]
[False, False, False, False, False, False, True, True, True, True]

会显得更简洁,可读性更强。

所以,不要有了锤子,看任何东西都是钉子。

关于 lambda 函数,后续还会用到。

7.4.2 map() 函数

map() 是 Python 内置函数,它的基本调用格式是(来自于帮助文档):

代码语言:javascript
复制
map(func, *iterables) --> map object
  • 参数 func 引用一个函数对象;
  • 参数 *iterables 收集多个可迭代对象。迭代器对象的成员依次作为 func 的实参。
  • 返回值 map object 是一个迭代器对象。

例如7.4.1节中演示过的一个并不太好的写法:

代码语言:javascript
复制
>>> [(lambda x: x>0)(n) for n in range(-5, 5)]
[False, False, False, False, False, False, True, True, True, True]

map() 函数改写,会显得更紧凑简洁。

代码语言:javascript
复制
>>> m = map(lambda x: x > 0, range(-5, 5))
>>> m
<map object at 0x7fe1585559a0>
>>> list(m)
[False, False, False, False, False, False, True, True, True, True]

变量 m 引用的对象即为 map() 函数的返回值——迭代器对象(参阅第9章9.6节),通过 list(m) 可以查看迭代器对象的成员,与之前所得一样。类似的操作还可以有:

代码语言:javascript
复制
>>> r = map(lambda x: x**2, range(0, 20, 2))
>>> list(r)
[0, 4, 16, 36, 64, 100, 144, 196, 256, 324]

显然,上述结果也可以使用列表解析的方式得到:

代码语言:javascript
复制
>>> [i ** 2 for i in range(0, 20, 2)]
[0, 4, 16, 36, 64, 100, 144, 196, 256, 324]

很多时候,列表解析与 map() 函数可以互相替代,在实际应用时读者可以根据情况选择。

再举一个例子,假设有三个列表,lst1 = [1, 2, 3, 4, 5], lst2 = [6, 7, 8, 9, 0], lst3 = [7, 8, 9, 2, 1] ,将这三个列表中对应成员相加。根据前面的经验,至少可以有两种实现方式(请读者自行先尝试,再看下面的代码)。

代码语言:javascript
复制
>>> lst1 = [1, 2, 3, 4, 5]
>>> lst2 = [6, 7, 8, 9, 0]
>>> lst3 = [7, 8, 9, 2, 1]

# 列表解析
>>> [x + y + z for x, y, z in zip(lst1, lst2, lst3)]
[14, 17, 20, 15, 6]

# map() 函数
>>> r = map(lambda x, y, z : x + y + z, lst1, lst2, lst3)
>>> list(r)
[14, 17, 20, 15, 6]

这个示例中 map() 函数的 *iterables 参数收集了三个可迭代对象。

此外,参数 func 不一定总是 lambda 函数,任何函数对象均可以。

代码语言:javascript
复制
>>> def add(x):
...     return x ** 2
...
>>> list(map(add, range(0, 10)))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

7.4.3 filter() 函数

单词 filter 的中文含义是“过滤器”,在 Python 中,内置 fileter() 函数的作用也是如此,其帮助文档显示:

代码语言:javascript
复制
filter(function or None, iterable) --> filter object

第一个参数是函数对象或者 None ,第二个参数是可迭代对象。执行 filter() 函数,可迭代对象中的成员传给前面的函数对象,作为其实参。如果该函数返回的是 True ,那么这个成员就被放到一个名为“filter object”的迭代器中, filter() 函数最后返回的就是这个迭代器对象。

例如:

代码语言:javascript
复制
>>> f = filter(lambda x: x > 0, range(-5, 5))
>>> f
<filter object at 0x7fe15822a700>
>>> list(f)
[1, 2, 3, 4]

变量 f 引用的对象就是 filter() 函数返回的 filter 对象,通过 list() 转化之后看到所包含的成员,的确是根据所定义的 lambda 函数筛选之后的数值。

当然,时刻不要忘记,列表解析还是继续可用。

代码语言:javascript
复制
>>> [i for i in range(-5, 5) if i > 0]
[1, 2, 3, 4]

对于 filter()function 的实参,除了 lambda 函数之外,也可以是任何函数对象。

代码语言:javascript
复制
>>> langs = ['python', 'PHP', 'Java', 'PER', 'Go']
>>> def all_caps(s):
...     return s.isupper()
...
>>> list(filter(all_caps, langs))
['PHP', 'PER']

当然,上面的代码也可以用 lambda 函数改写,请读者自行尝试。

本节借用函数式编程的名义,介绍了 map()filter() 两个内置函数以及 lambda 函数。从内容中读者也能认识到,这些函数均可以用以往学过的函数、列表解析等替代。所以,它们并非编程中的必须,只是可选项。

7.4.4 运用内置函数

第3章3.3.1节曾简要介绍了与数学运算相关的 Python 内置函数,其实本节中的 map()filter() 也是内置函数的一员。Python 内置函数所针对的通常是一些基础需求或问题,在自定义函数中,如果恰当使用内置函数,不仅能缩短代码行数,更能增强可读性,哪至于优化性能。例如写一个函数判断列表容器中的字符串成员是否有回文(关于回文,请参阅第4章4.2.5节),下面的函数 contains_palindrome() 是一种可行的方法:

代码语言:javascript
复制
#coding:utf-8
'''
filename: palindrome.py
'''

def contains_palindrome(words):
    for word in words:
        if word == ''.join(reversed(word)):
            return True
    return False

if __name__ == '__main__':
    lst = ['why', 'your', 'eye', 'is', 'large']
    print(contains_palindrome(lst))

程序的执行结果:

代码语言:javascript
复制
% python palindrome.py
True

字符串 lst 中的成员 'eye' 是回文,因此 contains_palindrome() 返回了 True 。下面要用内置函数 any() 改造 contains_palindrome() 函数内的代码。

代码语言:javascript
复制
def contains_palindrome_s(words):
    return any(word == ''.join(reversed(word)) for word in words)

读者通过 help(any) 不难理解 any() 的作用。

代码语言:javascript
复制
>>> lst = ['why', 'your', 'eye', 'is', 'large']
>>> b = [word == ''.join(reversed(word)) for word in lst]
>>> b
[False, False, True, False, False]
>>> any(b)
True

[word == ''.join(reversed(word)) for word in lst] 以列表解析得到了一个用布尔值标识 lst 中的成员是否式回文的列表,在以此列表为 any() 的参数,则返回 True

但是,在函数 contains_palindrome_s() 中并没有使用列表解析,而是使用了第9章9.7节将要学习的生成器解析,其中道理请参阅该节内容。

any() 函数类似的另外一个内置函数是 all() ,留个读者探索使用它的时机。

除了这两个内置函数之外,其他内置函数也当然要在编程中恰当应用。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-06-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 老齐教室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 7.4 函数式编程
    • 7.4.1 lambda 函数
      • 7.4.2 map() 函数
        • 7.4.3 filter() 函数
          • 7.4.4 运用内置函数
          相关产品与服务
          容器服务
          腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档