全部版块 我的主页
论坛 数据科学与人工智能 数据分析与数据科学 数据分析与数据挖掘
931 0
2020-10-14
Fn.py:享受Python中的函数式编程
尽管Python不是一种纯函数式编程语言,但它是多种范例,它为您提供了充分的自由来利用函数式编程方法。函数样式在理论上和实践上都有优势(您可以在Python文档中找到此列表):
形式可证明性
模块化
可组合性
易于调试和测试
尽管此列表具有足够的描述性,但我真的很喜欢Michael O. Church在其文章“功能性程序很少腐烂”中对功能性编程的优势的描述。我在Pycon UA 2012上讨论了在Python中使用函数方法:使用Python进行函数编程,并且提到了很多问题,您可能很快就会发现,尝试使用Python编写可读性和可维护的函数代码。
创建了库fn.py来处理这些问题。尽管不可能解决所有问题,但该库为您提供了缺失的“电池”,即使在大多数命令程序中也可以从功能方法中获得最大价值。你会发现什么呢?
Scala样式的lambda定义
map(lambda x:x * 2,[1
斯卡拉
清单(1
Clojure
(地图#(*%2)'(1 2 3))
哈斯克尔
地图(2 *)[1
Fn.py提供了特殊的_对象来简化lambda语法(受Scala的启发)。
从fn import _
断言(_ + _)(10
断言 列表(地图(_ *  2,范围(5)))== [ 0,2,4,6,8 ]
断言 列表(过滤器(_ <  10, [ 9,10,11 ]))== [ 9 ]
在许多其他情况下,您可以使用_:所有算术运算,属性解析,方法调用,切片。如果不确定函数要做什么,可以打印它:
从fn import _
打印(_ +  2)#“(x1)=>(x1 + 2)”
打印(_ + _ * _)#“(x1,x2,x3)=>(x1 +(x2 * x3))”
流和无限序列声明
延迟评估的Scala样式流。基本思想:“按需”评估每个新元素,并在所有创建的迭代器之间共享计算出的元素。流对象支持<<运算符,这意味着在必要时推送新元素。
惰性评估流是处理无限序列的强大抽象。让我们看看如何用函数式编程语言计算斐波那契数列:
哈斯克尔
fibs = 0:1:zipWith(+)fibs(tail fibs)
Clojure
(def fib(懒猫[0 1](地图+ fib(rest fib))))
斯卡拉
def fibs:Stream [Int] =
     0#:: 1#:: fibs.zip(fibs.tail).map {case(a,b)=> a + b}
现在,您可以在Python中执行相同的操作:
从FN进口流
从fn.iters进口冒,滴,地图
,从运营商进口加
f = Stream()
FIB = ?F << [ 0,1 ] << 地图(ADD,F,降(1,f))的
断言 列表(取(10,FIB))== [ 0,1,1,2,3,5,8,13,21,34 ]
断言FIB [ 20 ] ==  6765
断言 列表(FIB [ 30:35 ] )== [ 832040,1346269,2178309,3524578,5702887 ]
蹦床装饰
fn.recur.tco是在不占用大量堆栈的情况下处理TCO的解决方法。让我们从递归阶乘计算的简单示例开始:
def  fact(n):
     如果n ==  0:返回 1
     返回n * fact(n - 1)
这种变体有效,但是确实很丑陋。为什么?它将利用内存太重的原因来递归存储所有先前的值来计算最终结果。如果您将使用大n执行此函数(更多sys.getrecursionlimit ()),CPython将失败并显示
>>> 进口SYS
>>>事实(SYS 。 getrecursionlimit()*  2)
...堆栈跟踪的很多很多行...
RuntimeError:最大递归深度超过
这样做很好,因为它可以防止您在代码中犯下严重的错误。
我们如何优化该解决方案?答案很简单,让transform函数使用尾调用:
def  fact(n,acc = 1):
     如果n ==  0:返回acc
     返回事实(n - 1,acc * n)
为什么这个变体更好?因为您无需记住先前的值即可计算最终结果。有关Wikipedia上的尾部呼叫优化的更多信息。但是... Python解释器将以与上一个相同的方式执行此功能,因此您将一无所获。
fn.recur.tco为您提供了使用“蹦床”方法编写“优化一点”尾调用递归的机制。例如,在Clojure中使用了相同的方法,主要思想是将功能调用的序列扩展为while循环。
从fn导入重复
@ recur.tco
def  fact(n,acc = 1):
     如果n ==  0:返回 False,acc
     返回 True,(n - 1,acc * n)
@ recur.tco是一个装饰器,它在while循环中执行功能并检查输出:
(错误,结果)表示我们完成了
(True,args,kwargs)意味着我们需要使用其他参数再次调用函数
(func,args,kwargs)切换要在while循环内执行的功能
错误处理的功能风格
假设您有Request类,该类通过其名称为您提供参数值。要获得非空条带化值的大写表示法:
class  Request(dict):
      def 参数(self,name):
         返回 self 。get(name,None)
r =请求(测试= “固定”,空= ““))
参数= r 。如果param为None,则为parameter(“ testing”)

     固定=  “”
其他:     
     param = param 。如果 len(param)== 0则strip()
      :
         固定=  “”
     其他:
        固定=参数。上()
嗯,看起来很丑。使用fn.monad.Option更新代码。它代表可选值,每个Option实例可以是Full或Empty实例(受Scala Option启发)。它为您提供了一种编写长计算序列并摆脱许多if / else块的简单方法。
从运营商进口methodcaller
从fn.monad进口optionable
类 Request(dict):
     @optionable
     def 参数(self,name):
         返回 self 。get(name,None)
r =请求(测试= “固定”,空= ““))
固定= - [R 。参数(“测试”)
           。map(methodcaller(“ strip”))
           。filter(len)
           。map(methodcaller(“ upper”))
           。get_or(“”)
fn.monad.Option.or_call是尝试几种变体以结束计算的好方法。即使用具有带有可选属性type,mimetype和url的Request类。您需要使用至少一个属性来评估“请求类型”:
从fn.monad导入选项
request =  dict(url = “ face.png”,mimetype = “ PNG”)
tp =选项\
          。from_value(请求。获得(“类型”,无))\ #检查“式的”关键第一
         。or_call(from_mimetype,request)\ #或..检查“ mimetype”键
         。or_call(from_extension,request)\ #或...获取“ url”并检查extension
         。get_or(“应用程序/未定义”)
还有吗
我仅描述了库功能的一小部分。您也可以找到并使用:
22种其他itertools配方可扩展内置模块的功能
Python 2和Python 3(范围,地图,过滤器等)的迭代器统一,这在跨版本库上工作时非常有用
用于功能组合和部分功能应用的简单语法
其他运算符可使用高级功能(应用,翻转等)
工作正在进行中
自从在Github上发布该库以来,我得到了社区的许多评论,想法和建议以及补丁和修复。我将继续致力于现有功能和新功能的增强。在最接近的路线图中:
更多运算符与可迭代对象一起使用,例如foldl,foldr
更多的单子,即fn.monad.Either处理错误日志
大多数模块的C加速器
咖喱函数生成器以简化lambda arg1:lambda arg2:...
更多文档,更多测试,更多示例

关注 CDA人工智能学院 ,回复“录播”获取更多人工智能精选直播视频!


二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

相关推荐
栏目导航
热门文章
推荐文章

说点什么

分享

扫码加好友,拉您进群
各岗位、行业、专业交流群