尽管Python事实上并不是一门纯函数式编程语言,但它本身是一门多范型语言,并给了你足够的自由利用函数式编程的便利。函数式风格有着各种理论与实际上的好处(你可以在Python的文档中找到这个列表):

形式上可证

模块性

组合性

易于调试及测试

虽然这份列表已经描述得够清楚了,但我还是很喜欢Michael O.Church在他的文章“函数式程序极少腐坏(Functional programs rarely rot)”中对函数式编程的优点所作的描述。我在PyCon UA 2012期间的讲座“Functional Programming with Python”中谈论了在Python中使用函数式方式的内容。我也提到,在你尝试在Python中编写可读同时又可维护的函数式代码时,你会很快发现诸多问题。

fn.py类库就是为了应对这些问题而诞生的。尽管它不可能解决所有问题,但对于希望从函数式编程方式中获取最大价值的开发者而言,它是一块“电池”,即使是在命令式方式占主导地位的程序中,也能够发挥作用。那么,它里面都有些什么呢?

Scala风格的Lambda定义

在Python中创建Lambda函数的语法非常冗长,来比较一下:

Python

map(lambda x: x*2, [1,2,3])

Scala

复制代码 代码如下:

List(1,2,3).map(_*2)

Clojure

复制代码 代码如下:

(map #(* % 2) '(1 2 3))

Haskell

复制代码 代码如下:

map (2*) [1,2,3]

受Scala的启发,Fn.py提供了一个特别的_对象以简化Lambda语法。

from fn import _

assert (_ + _)(10, 5) = 15

assert list(map(_ * 2, range(5))) == [0,2,4,6,8]

assert list(filter(_ < 10, [9,10,11])) == [9]

除此之外还有许多场景可以使用_:所有的算术操作、属性解析、方法调用及分片算法。如果你不确定你的函数具体会做些什么,你可以将结果打印出来:

from fn import _

print (_ + 2) # "(x1) => (x1 + 2)"

print (_ + _ * _) # "(x1, x2, x3) => (x1 + (x2 * x3))"

流(Stream)及无限序列的声明

Scala风格的惰性求值(Lazy-evaluated)流。其基本思路是:对每个新元素“按需”取值,并在所创建的全部迭代中共享计算出的元素值。Stream对象支持<<操作符,代表在需要时将新元素推入其中。

惰性求值流对无限序列的处理是一个强大的抽象。我们来看看在函数式编程语言中如何计算一个斐波那契序列。

Haskell

复制代码 代码如下:

fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

Clojure

复制代码 代码如下:

(def fib (lazy-cat [0 1] (map + fib (rest fib))))

Scala

复制代码 代码如下:

def fibs: Stream[Int] =

0 #:: 1 #:: fibs.zip(fibs.tail).map{case (a,b) => a + b}

现在你可以在Python中使用同样的方式了:

from fn import Stream

from fn.iters import take, drop, map

from operator import add

f = Stream()

fib = f << [0, 1] << map(add, f, drop(1, f))

assert list(take(10, fib)) == [0,1,1,2,3,5,8,13,21,34]

assert fib[20] == 6765

assert list(fib[30:35]) == [832040,1346269,2178309,3524578,5702887]

蹦床(Trampolines)修饰符

fn.recur.tco是一个不需要大量栈空间分配就可以处理TCO的临时方案。让我们先从一个递归阶乘计算示例开始:

def fact(n):

if n == 0: return 1

return n * fact(n-1)

这种方式也能工作,但实现非常糟糕。为什么呢?因为它会递归式地保存之前的计算值以算出最终结果,因此消耗了大量的存储空间。如果你对一个很大的n值(超过了sys.getrecursionlimit()的值)执行这个函数,CPython就会以此方式失败中止:

>>> import sys

>>> fact(sys.getrecursionlimit() * 2)

... many many lines of stacktrace ...

RuntimeError: maximum recursion depth exceeded

这也是件好事,至少它避免了在你的代码中产生严重错误。

我们如何优化这个方案呢?答案很简单,只需改变函数以使用尾递归即可:

def fact(n, acc=1):

if n == 0: return acc

return fact(n-1, acc*n)

为什么这种方式更佳呢?因为你不需要保留之前的值以计算出最终结果。可以在Wikipedia上查看更多尾递归调用优化的内容。可是……Python的解释器会用和之前函数相同的方式执行这段函数,结果是你没得到任何优化。

fn.recur.tco为你提供了一种机制,使你可以使用“蹦床”方式获得一定的尾递归优化。同样的方式也使用在诸如Clojure语言中,主要思路是将函数调用序列转换为while循环。

from fn import recur

@recur.tco

def fact(n, acc=1):

if n == 0: return False, acc

return True, (n-1, acc*n)

@recur.tco是一个修饰符,能将你的函数执行转为while循环并检验其输出内容:

(False, result)代表运行完毕

(True, args, kwargs)代表我们要继续调用函数并传递不同的参数

(func, args, kwargs)代表在while循环中切换要执行的函数

函数式风格的错误处理

假设你有一个Request类,可以按照传入其中的参数名称得到对应的值。要想让其返回值格式为全大写、非空并且去除头尾空格的字符串,你需要这样写:

class Request(dict):

def parameter(self, name):

return self.get(name, None)

r = Request(testing="Fixed", empty=" ")

param = r.parameter("testing")

if param is None:

fixed = ""

else:

param = param.strip()

if len(param) == 0:

fixed = ""

else:

fixed = param.upper()

额,看上去有些古怪。用fn.monad.Option来修改你的代码吧,它代表了可选值,每个Option实例可代表一个Full或者Empty(这点也受到了Scala中Option的启发)。它为你编写长运算序列提供了简便的方法,并且去掉除了许多if/else语句块。

from operator import methodcaller

from fn.monad import optionable

class Request(dict):

@optionable

def parameter(self, name):

return self.get(name, None)

r = Request(testing="Fixed", empty=" ")

fixed = r.parameter("testing")

.map(methodcaller("strip"))

.filter(len)

.map(methodcaller("upper"))

.get_or("")

fn.monad.Option.or_call是个便利的方法,它允许你进行多次调用尝试以完成计算。例如,你有一个Request类,它有type,mimetype和url等几个可选属性,你需要使用最少一个属性值以分析它的“request类型”:

from fn.monad import Option

request = dict(url="face.png", mimetype="PNG")

tp = Option \

.from_value(request.get("type", None)) \ # check "type" key first

.or_call(from_mimetype, request) \ # or.. check "mimetype" key

.or_call(from_extension, request) \ # or... get "url" and check extension

.get_or("application/undefined")

其余事项?

我仅仅描述了类库的一小部分,你还能够找到并使用以下功能:

22个附加的itertools代码段,以扩展内置module的功能的附加功能

将Python 2和Python 3的迭代器(iterator)(如range,map及filtter等等)使用进行了统一,这对使用跨版本的类库时非常有用

为函数式组合及partial函数应用提供了简便的语法

为使用高阶函数(apply,flip等等)提供了附加的操作符

正在进行中的工作

自从在Github上发布这个类库以来,我从社区中收到了许多审校观点、意见和建议,以及补丁和修复。我也在继续增强现有功能,并提供新的特性。近期的路线图包括以下内容:

为使用可迭代对象(iterable),如foldl,foldr增加更多操作符

更多的monad,如fn.monad.Either,以处理错误记录

为大多数module提供C-accelerator

为简化lambda arg1: lambda arg2:…形式而提供的curry函数的生成器

更多文档,更多测试,更多示例代码

本文标题: 利用Fn.py库在Python中进行函数式编程

本文地址: http://www.cppcns.com/jiaoben/python/123280.html

python支持函数式编程吗_利用Fn.py库在Python中进行函数式编程相关推荐

  1. python标准库很丰富支持多种编程范式_基础班-第03天{python基础}

    声明:内容来源于网络,本人只是在此稍作整理,如有涉及版权问题,归传智播客官方所有. 认识python(了解) 1.Python发展历史 起源 Python的作者,Guido von Rossum,荷兰 ...

  2. python如何实现接口安全_利用pypy沙箱模式实现安全的开放式Python用户编程接口的方法与流程...

    本发明涉及人机交互技术领域,具体为一种利用pypy沙箱模式实现安全的开放式Python用户编程接口的方法. 背景技术: 编程是人与计算机交互最直接与高效的方式,由于其专业性与敏感性,一般产品不会提供该 ...

  3. python计算密集型提速_利用Cython加速计算密集型python任务

    何为计算密集型任务 下面贴上网上找到的描述计算密集型任务的特点是要进行大量的计算,消耗CPU资源,比如计算圆周率.对视频进行高清解码等等,全靠CPU的运算能力.这种计算密集型任务虽然也可以用多任务完成 ...

  4. python怎么去掉视频字幕_利用Python实现字幕挂载(把字幕文件与视频合并)思路详解...

    其实超简单超简单!python好现成的库,一下子省略了好多步骤! 本文在Windows环境下!linux只是不需要手动输入imagicmagick的位置! 需要用到的环境 python(基本上只要不是 ...

  5. python网页版本_利用jupyter网页版本进行python函数查询方式

    我就废话不多说了,还是直接看代码吧! import numpy world_alchol=numpy.genfromtxt("world_alcohol.txt",delimter ...

  6. python打代码运行图形_利用aardio给python编写图形界面

    前阵子在用python写一些小程序,写完后就开始思考怎么给python程序配一个图形界面,毕竟控制台实在太丑陋了. 于是百度了下python的图形界面库,眼花缭乱的一整页,拣了几件有"特色& ...

  7. python抓取数据包_利用python-pypcap抓取带VLAN标签的数据包方法

    1.背景介绍 在采用通常的socket抓包方式下,操作系统会自动将收到包的VLAN信息剥离,导致上层应用收到的包不会含有VLAN标签信息.而libpcap虽然是基于socket实现抓包,但在收到数据包 ...

  8. python支持哪些平台开发_【后端开发】python能兼容哪些平台

    目前Python可以说是相当的火爆了,网络爬虫,人工智能,数据挖掘与处理,金融量化交易等.那么Python都能运行在那些平台呢? Python支持常见的主流平台,如AIX.HPUX.Solaris.L ...

  9. python爬取网易云_利用python爬取网易云音乐,并把数据存入mysql

    作者:sergiojune Python爱好者社区--专栏作者 个人公众号:日常学python 专注python爬虫,数据可视化,数据分析,python前端技术 公众号:Python爱好者社区 获取本 ...

最新文章

  1. Imperva开源域目录控制器,简化活动目录集成
  2. java----数据结构与算法----JavaAPI:Map接口
  3. 按键映射_第三章 中文注释及按键相关
  4. 深度学习之循环神经网络(2)循环神经网络原理
  5. sde用户下使用sqlplus登录错误ORA-12547: TNS:lost contact
  6. 为什么开发人员要使用Linux
  7. Spark的新方案UnifiedMemoryManager内存管理模型分析
  8. build vue3 后压缩的文件在哪_Vue 3学习:4. 集成vuex
  9. android标题栏不被顶上去,Android仿微信QQ聊天顶起输入法不顶起标题栏的问题
  10. Fiddler如何捕捉DefaultHttpClient的HTTP请求
  11. Illustrator 教程,了解 AI 中的绘图工具
  12. python3扫描_Python3实现TCP端口扫描器
  13. linux 进程 堆大小,Linux进程的默认分配堆大小
  14. python源代码编译后的文件扩展名-python源代码被解释器转换后的格式是什么?
  15. vs code代码格式化配置
  16. 简单好听的id_简单好听的贴吧id名-网名搜索
  17. 五邑大学、广东工业大学教务系统一键评教代码分享及技术简易剖析
  18. force transducer and acceleration transducer
  19. 仿微信视频下载进度自定义View
  20. LATEX教程第四讲——那些不得不说的宏包(下)

热门文章

  1. 苹果“撞上”反垄断,围墙花园能否坚挺?
  2. 为什么我们拒绝使用 Docker
  3. 经纬张颖「炮轰」扫码点餐;淘宝特价版给拼多多送芒果;Firefox 87.0 发布|极客头条...
  4. 面试官问:在读多写少的情况下,如何优化 MySQL 的数据查询方案
  5. IEEE 迎来首位华人主席,马里兰大学终身教授刘国瑞当选
  6. 2020互联网岳麓峰会“软件再出发论坛”成功举办
  7. Rancher获4000万美元D轮融资,推动计算无处不在
  8. 达摩院成立XG实验室!阿里官宣进军5G
  9. 硬核 App,这项新功能一定要打开,关键时刻能救命!!
  10. 在编程中,为何说数据仍占主导地位?