美文网首页
Functional Programming in Python

Functional Programming in Python

作者: zhizhuwang | 来源:发表于2018-05-23 21:35 被阅读35次

    高阶函数(High-order Function)是函数式编程中非常重要的概念,它是提升代码抽象层次的重要方法和手段。越来越多的语言开始支持函数式编程的范式,比如Java、C++。
    虽然Python不是像Haskell这样纯粹的函数式编程语言,但是它也具有函数式编程的一些特性;而且Python现在应用非常广泛,了解一些这方面的特性,可以帮助我们写出更加简洁的代码。
    在介绍高阶函数之前,我们需要先弄清楚First ClassCurryClosure这几个概念。

    Function Object

    在Python中,有三种实现Function Object的形式:

    • def 关键字
    def add(a, b):
        return a + b
    
    >>> add(2,3)
    5
    
    • lambda关键字
    f = lambda a,b: a + b
    
    >>> f(2,3)
    5
    
    • 实现了__call__函数的Class
    class MyClass:
        def __call__(self, a, b):
            return a + b
    
    >>> cls = MyClass()
    
    >>> cls
    <__main__.MyClass at 0x5e9d358>
    
    >>> cls(2,3)
    5
    

    上述三种方式都可以实现一个add的函数,并且三者在功能上是等价的。

    由于函数是对象,所以,它可以被赋值给变量,可以被存储在数据结构中,可以作为函数的参数,还可以作为函数的返回值。
    函数作为一等公民(First Class)存在,是实现高阶函数(High-order function)的基础。

    Closure

    在说闭包之前先来认识自由变量。在某个作用域中,如果使用了未在本作用域中声明的变量,对于此作用域来说,该变量就是一个自由变量。闭包(Closure),就是指引用了自由变量的函数。
    以Python为例,可以这样来认识Closure

    def inc(x):
        def incx(y):
            return x + y
        return incx
    
    >>> inc2 = inc(2)
    >>> inc10 = inc(10)
    
    >>> inc2(2)
    4
    >>> inc10(2)
    12 
    

    在这个例子中,incx函数绑定了一个自由变量x

    Curry

    在Haskell中,所有的函数都只有一个参数,所有的多参数的函数都是柯里函数。柯里函数不会一次性取完所有参数,而是在每次调用是只取用一个参数,并返回一个一元函数,来取下一个参数,以此类推。

    以Haskell中的max函数为例:

    ghci> max 4 5
    5
    ghci> (max 4) 5
    5
    

    首先,max会被应用到4上,返回一个一元函数,随后以5为参数调用返回的一元函数,并在这一步得到最终结果。因此,上述两个调用是等价的。

    这样的好处是什么呢?简言之,我们只要以部分的参数来调用某函数,就可以得到一个部分应用(partial application)函数,部分应用函数所接受的参数的数量,和之前少传入的参数的数量一致。比如max 4,可以得到一个一元函数。部分应用使得构造新函数变得便捷简便,随时都可以为传递为其它函数而构造出新函数。

    在上面闭包的例子中,函数add(x,y)有两个入参,我们把它改写成了两个具有一个入参的函数。下面两种使用方式是等效的:

    >>> inc(2)(3)
    5
    
    >>> inc2(3)
    5
    

    高阶函数

    什么是高阶函数:

    • 是一个Function Object
    • 可以在函数中定义
    • 可以赋值给变量
    • 可以用作参数或返回值
    map,reduce,filter

    典型的高阶函数:mapreducefilter,也是几乎所有的函数式编程语言都会提供的函数,也是一种很重要的抽象手段。著名的MapReduce模型就来自于这个思想。

    >>> sq = map( lambda x: x ** 2, range(0,10) )
    
    >>> print([ i for i in sq ])
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    
    from functools import *
    >>> sq = map( lambda x: x ** 2, range(0,10) )
    
    >>> reduce(lambda x,y: x+y, sq)
    285
    
    >>> f = filter ( lambda x: x % 2 == 0, [ i for i in range(10) ])
    
    >>> f
    <filter at 0x50c56d8>
    
    >>> print([ i for i in f ])
    [0, 2, 4, 6, 8]
    

    在Python的3.x版本中,mapreducefilter函数会构造出一个Iterator,而不是list。

    Decorator

    Decorator(装饰器)是高阶函数的语法糖,一般用@符号标识。

    def makeitalic(fn):
         def wrapped(*args, **kwargs):
             return "<i>" + fn(*args, **kwargs) + "</i>"
         return wrapped
    
    def makebold(fn):
         def wrapped(*args, **kwargs):
             return "<b>" + fn(*args, **kwargs) + "</b>"
         return wrapped
    
    @makeitalic
    @makebold  # hello = makeitalic(makebold(hello))
    def hello(*args, **kwargs):
         return "Hello. args: {}, kwargs: {}".format(args, kwargs)
    
    >>> print( hello( 'hello', 'world', demo='generator'))
    <i><b>Hello. args: ('hello', 'world'), kwargs: {'demo': 'generator'}</b></i>
    

    在上面的这个示例中,分别定义了makeitalicmakebold两个decorator,对hello函数进行相应的装饰。比较特别的是,decorator之间也可以进行组合,在上面这个示例中,它们组合起来完成了italicbold的功能。从本质上可以理解为,这是一个多层嵌套的高阶函数。

    此外,Decorator还可以带参数,可以是实现了__call__的可调用对象,可以是带有不同参数的多个可调用对象的实例。

    class BoldMaker:
        def __init__(self, fn):
            self.fn = fn
    
        def __call__(self, *args, **kwargs):
            return "<b>" + self.fn(args, kwargs) + "</b>"
    
    @BoldMaker
    def hello(*args, **kwargs):
        return "Hello. args: {}, kwargs: {}".format(args, kwargs)
    
    >>> print(hello("This is decorator of callable class"))
    <b>Hello. args: (('This is decorator of callable class',), {}), kwargs: {}</b>
    

    Decorator也可以带一个或者多个参数,例如:

    def  enclose_in_tags(opening_tag, closing_tag):
        def make_with_tags(fn):
            def wrapped():
                return opening_tag + fn() + closing_tag
            return wrapped
        return make_with_tags
    
    @enclose_in_tags(opening_tag='<h1>', closing_tag='</h1>')
    def hello():
        return "hello world"
    
    >>> hello()
    '<h1>hello world</h1>'
    

    参考资料

    谈谈闭包——以Swift为例
    Using functional programming in Python like a boss: Generators, Iterators and Decorators
    理解 Python 装饰器就看这一篇
    Higher Order Functions: Using Filter, Map and Reduce for More Maintainable Code

    相关文章

      网友评论

          本文标题:Functional Programming in Python

          本文链接:https://www.haomeiwen.com/subject/nmkwxftx.html