这大概算是Python最难啃的一块骨头吧。在我Python生涯的这一年里,我遇到了一些Pythoner,他们毫无例外地完全不会使用函数式编程(有些人喜欢称为Pythonic),比如,从来不会传递函数,不知道lambda是什么意思,知道列表展开但从来不知道用在哪里,对Python不提供经典for循环感到无所适从,言谈之中表现出对函数式风格的一种抗拒甚至厌恶。

我尝试剖析这个问题,最终总结了这么两个原因:1、不想改变,认为现有的知识可以完成任务;2、对小众语言的歧视,Python目前在国内市场份额仍然很小很小,熟悉Python风格用处不大。

然而我认为,学习使用一种截然不同的风格可以颠覆整个编程的思想。我会慢慢总结一个系列共4篇文字,篇幅都不大,轻松就能看完,希望对喜欢Python的人们有所帮助,因为我个人确实从中受益匪浅。

还是那句老话,尊重作者的劳动,转载请注明原作者和原地址:)

1. 函数式编程概述

1.1. 什么是函数式编程?

函数式编程使用一系列的函数解决问题。函数仅接受输入并产生输出,不包含任何能影响产生输出的内部状态。任何情况下,使用相同的参数调用函数始终能产生同样的结果。

在一个函数式的程序中,输入的数据“流过”一系列的函数,每一个函数根据它的输入产生输出。函数式风格避免编写有“边界效应”(side effects)的函数:修改内部状态,或者是其他无法反应在输出上的变化。完全没有边界效应的函数被称为“纯函数式的”(purely functional)。避免边界效应意味着不使用在程序运行时可变的数据结构,输出只依赖于输入。

可以认为函数式编程刚好站在了面向对象编程的对立面。对象通常包含内部状态(字段),和许多能修改这些状态的函数,程序则由不断修改状态构成;函数式编程则极力避免状态改动,并通过在函数间传递数据流进行工作。但这并不是说无法同时使用函数式编程和面向对象编程,事实上,复杂的系统一般会采用面向对象技术建模,但混合使用函数式风格还能让你额外享受函数式风格的优点。

1.2. 为什么使用函数式编程?

函数式的风格通常被认为有如下优点:

  • 逻辑可证
    这是一个学术上的优点:没有边界效应使得更容易从逻辑上证明程序是正确的(而不是通过测试)。
  • 模块化
    函数式编程推崇简单原则,一个函数只做一件事情,将大的功能拆分成尽可能小的模块。小的函数更易于阅读和检查错误。
  • 组件化
    小的函数更容易加以组合形成新的功能。
  • 易于调试
    细化的、定义清晰的函数使得调试更加简单。当程序不正常运行时,每一个函数都是检查数据是否正确的接口,能更快速地排除没有问题的代码,定位到出现问题的地方。
  • 易于测试
    不依赖于系统状态的函数无须在测试前构造测试桩,使得编写单元测试更加容易。
  • 更高的生产率
    函数式编程产生的代码比其他技术更少(往往是其他技术的一半左右),并且更容易阅读和维护。
1.3. 如何辨认函数式风格?

支持函数式编程的语言通常具有如下特征,大量使用这些特征的代码即可被认为是函数式的:

  • 函数是一等公民
    函数能作为参数传递,或者是作为返回值返回。这个特性使得模板方法模式非常易于编写,这也促使了这个模式被更频繁地使用。
    以一个简单的集合排序为例,假设lst是一个数集,并拥有一个排序方法sort需要将如何确定顺序作为参数。
    如果函数不能作为参数,那么lst的sort方法只能接受普通对象作为参数。这样一来我们需要首先定义一个接口,然后定义一个实现该接口的类,最后将该类的一个实例传给sort方法,由sort调用这个实例的compare方法,就像这样:

    #伪代码
    interface Comparator {compare(o1, o2)
    }
    lst = list(range(5))
    lst.sort(Comparator() {compare(o1, o2) {return o2 - o1 //逆序
    })

    可见,我们定义了一个新的接口、新的类型(这里是一个匿名类),并new了一个新的对象只为了调用一个方法。如果这个方法可以直接作为参数传递会怎样呢?看起来应该像这样:

    def compare(o1, o2): return o2 - o1 #逆序
    lst = list(range(5))
    lst.sort(compare) 

    请注意,前一段代码已经使用了匿名类技巧从而省下了不少代码,但仍然不如直接传递函数简单、自然。

  • 匿名函数(lambda)
    lambda提供了快速编写简单函数的能力。对于偶尔为之的行为,lambda让你不再需要在编码时跳转到其他位置去编写函数。
    lambda表达式定义一个匿名的函数,如果这个函数仅在编码的位置使用到,你可以现场定义、直接使用:

    lst.sort(lambda o1, o2: o1.compareTo(o2))

    相信从这个小小的例子你也能感受到强大的生产效率:)

  • 封装控制结构的内置模板函数
    为了避开边界效应,函数式风格尽量避免使用变量,而仅仅为了控制流程而定义的循环变量和流程中产生的临时变量无疑是最需要避免的。
    假如我们需要对刚才的数集进行过滤得到所有的正数,使用指令式风格的代码应该像是这样:

    lst2 = list()
    for i in range(len(lst)): #模拟经典for循环if lst[i] > 0:lst2.append(lst[i])

    这段代码把从创建新列表、循环、取出元素、判断、添加至新列表的整个流程完整的展示了出来,俨然把解释器当成了需要手把手指导的傻瓜。然而,“过滤”这个动作是很常见的,为什么解释器不能掌握过滤的流程,而我们只需要告诉它过滤规则呢?
    在Python里,过滤由一个名为filter的内置函数实现。有了这个函数,解释器就学会了如何“过滤”,而我们只需要把规则告诉它:

    lst2 = filter(lambda n: n > 0, lst)

    这个函数带来的好处不仅仅是少写了几行代码这么简单。
    封装控制结构后,代码中就只需要描述功能而不是做法,这样的代码更清晰,更可读。因为避开了控制结构的干扰,第二段代码显然能让你更容易了解它的意图。
    另外,因为避开了索引,使得代码中不太可能触发下标越界这种异常,除非你手动制造一个。
    函数式编程语言通常封装了数个类似“过滤”这样的常见动作作为模板函数。唯一的缺点是这些函数需要少量的学习成本,但这绝对不能掩盖使用它们带来的好处。

  • 闭包(closure)
    闭包是绑定了外部作用域的变量(但不是全局变量)的函数。大部分情况下外部作用域指的是外部函数。
    闭包包含了自身函数体和所需外部函数中的“变量名的引用”。引用变量名意味着绑定的是变量名,而不是变量实际指向的对象;如果给变量重新赋值,闭包中能访问到的将是新的值。
    闭包使函数更加灵活和强大。即使程序运行至离开外部函数,如果闭包仍然可见,则被绑定的变量仍然有效;每次运行至外部函数,都会重新创建闭包,绑定的变量是不同的,不需要担心在旧的闭包中绑定的变量会被新的值覆盖。
    回到刚才过滤数集的例子。假设过滤条件中的 0 这个边界值不再是固定的,而是由用户控制。如果没有闭包,那么代码必须修改为:

    class greater_than_helper:def __init__(self, minval):self.minval = minvaldef is_greater_than(self, val):return val > self.minvaldef my_filter(lst, minval):helper = greater_than_helper(minval)return filter(helper.is_greater_than, lst)

    请注意我们现在已经为过滤功能编写了一个函数my_filter。如你所见,我们需要在别的地方(此例中是类greater_than_helper)持有另一个操作数minval。
    如果支持闭包,因为闭包可以直接使用外部作用域的变量,我们就不再需要greater_than_helper了:

    def my_filter(lst, minval): return filter(lambda n: n > minval, lst) 

    可见,闭包在不影响可读性的同时也省下了不少代码量。
    函数式编程语言都提供了对闭包的不同程度的支持。在Python 2.x中,闭包无法修改绑定变量的值,所有修改绑定变量的行为都被看成新建了一个同名的局部变量并将绑定变量隐藏。Python 3.x中新加入了一个关键字 nonlocal 以支持修改绑定变量。但不管支持程度如何,你始终可以访问(读取)绑定变量。

  • 内置的不可变数据结构
    为了避开边界效应,不可变的数据结构是函数式编程中不可或缺的部分。不可变的数据结构保证数据的一致性,极大地降低了排查问题的难度。
    例如,Python中的元组(tuple)就是不可变的,所有对元组的操作都不能改变元组的内容,所有试图修改元组内容的操作都会产生一个异常。
    函数式编程语言一般会提供数据结构的两种版本(可变和不可变),并推荐使用不可变的版本。
  • 递归
    递归是另一种取代循环的方法。递归其实是函数式编程很常见的形式,经常可以在一些算法中见到。但之所以放到最后,是因为实际上我们一般很少用到递归。如果一个递归无法被编译器或解释器优化,很容易就会产生栈溢出;另一方面复杂的递归往往让人感觉迷惑,不如循环清晰,所以众多最佳实践均指出使用循环而非递归。
    这一系列短文中都不会关注递归的使用。

<第一节完>

转载于:https://www.cnblogs.com/huxi/archive/2011/06/18/2084316.html

Python函数式编程指南(一):概述相关推荐

  1. Python函数式编程指南

    http://www.cnblogs.com/huxi/archive/2011/06/18/2084316.html 1. 函数式编程概述 1.1. 什么是函数式编程? 函数式编程使用一系列的函数解 ...

  2. Python函数式编程指南(二):函数

    这是此系列的第二篇,试图说明在Python中如何更好地使用函数并引导诸位使用函数式的思维进行思考.掌握并应用这些内容,就已经是至少形似的函数式风格的代码了,至于思维么,这个真靠自己. 作者水平有限,如 ...

  3. 《Python面向对象编程指南》——1.2 基类中的__init__()方法

    本节书摘来自异步社区<Python面向对象编程指南>一书中的第1章,第1.2节,作者[美]Steven F. Lott, 张心韬 兰亮 译,更多章节内容可以访问云栖社区"异步社区 ...

  4. 函数式编程语言python-10分钟学会python函数式编程

    原标题:10分钟学会python函数式编程 在这篇文章里,你将学会什么是函数范式以及如何使用Python进行函数式编程.你也将了解列表推导和其它形式的推导. 函数范式 在命令式范式中,通过为计算机提供 ...

  5. Python函数式编程——map()、reduce()

    提起map和reduce想必大家并不陌生,Google公司2003年提出了一个名为MapReduce的编程模型[1],用于处理大规模海量数据,并在之后广泛的应用于Google的各项应用中,2006年A ...

  6. python函数式编程读取数据-python学习笔记9:函数式编程

    函数式编程(FunctionalProgramming) 基于lambda演算的一种编程方式 程序中只有函数 函数可以作为参数,同样可以作为返回值 纯函数式编程语言: LISP, Haskell Py ...

  7. python编写函数_浅谈Python 函数式编程

    匿名函数lambda表达式 什么是匿名函数? 匿名函数,顾名思义就是没有名字的函数,在程序中不用使用 def 进行定义,可以直接使用 lambda 关键字编写简单的代码逻辑.lambda 本质上是一个 ...

  8. Python函数式编程简介(二)返回函数

    本文为Python函数式编程的第二部分主要讲解返回函数和闭包 2.返回函数 2.1.函数作为返回值 高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回. 我们来实现一个可变参数的求和.通常 ...

  9. Python函数式编程简介(一)高阶函数

    本文概括介绍Python函数式编程的一些概念及用法,详细请参考: https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df ...

最新文章

  1. 思科ucs-b系列服务器,思科统一计算系统UCS B系列刀片推荐
  2. Python学习笔记——基础篇【第七周】———FTP作业(面向对象编程进阶 Socket编程基础)...
  3. oracle 表关联索引优化,Oracle执行计划调优-超级大表关联超级小表的性能调优
  4. 决策树编程python_Python如何制定决策:编程中的控制流简介
  5. vue2自定义分页组件,可设置每页显示数量,指定跳转具体页面
  6. artcam 9.0英文版本下载_Jenkins版本升级(修复漏洞)
  7. redis 数据类型详解 以及 redis适用场景场合
  8. SpringBoot之集成MybatisPlus
  9. [VB.NET]请教一个如何对姓名进行同音字查询?
  10. java移位操作示例
  11. oppo如何用计算机找回锁屏密码,OPPO忘了锁屏密码怎么办?从解锁到恢复全过程...
  12. React+Dva使用d3绘图
  13. 人人都是架构师:分布式系统架构落地与瓶颈突破 - china-pub网上书店
  14. Openstack 组件Placement部署思路过程
  15. 基于翻译的模型-TransE,TransH,TransR,TransD
  16. 图的广度优先搜索--python实现
  17. 如何绘制流程图?绘制流程图在线网站分享
  18. 模拟器提示关闭 hyper-V,但 hyper-V实际上并没有开启
  19. 【Quant】BigQuant学习笔记(1) 可视化策略
  20. WORD精灵:将Word文档中的标点符号统一为全角或者半角

热门文章

  1. 好家伙,这才是最强的目标检测落地模型!
  2. 巴塞尔大学合作项目博士招生,医学图像分析领域
  3. 准确估计透明物体的 3D 姿态:机器人收集 + Keypose 算法
  4. 720p/30fps,谷歌人体分割模型Bodypix再次更新,针对Coral开发板优化
  5. redis java连接出错_redis连接错误与spring boot
  6. Python、TensorFlow、机器学习、深度学习四件套(附免费下载)
  7. Facebook田渊栋谈人生挑战与选择
  8. Python中append()和extend方法的使用和区别
  9. 数据科学与计算机学院张治国,张治国(河海大学机电工程学院副教授)_百度百科...
  10. 大数据应用项目创新大赛_第二届海南大数据创新应用大赛收官