• 函数式编程之 Python


    上接 python 函数式编程学习笔记

    参考:www.sigai.cn/

    1 函数式编程概述

    • 前提:函数在 Python 中是⼀等对象
    • 工具:built-in ⾼阶函数;lambda 函数;operator 模块;functools 模块
    • 模式:闭包与装饰器
    • 替代:⽤用 List Comprehension 可轻松替代 map 和 filter(reduce 替代起来⽐比较困难)
    • 原则:No Side Effect

    何为 No Side Effect? 函数的所有功能就仅仅是返回一个新的值而已,没有其他行为,尤其是不得修改外部变量。因⽽,各个独⽴的部分的执⾏顺序可以随意打乱,带来执⾏顺序上的⾃自使得⼀系列新的特性得以实现:⽆锁的并发;惰性求值;编译器器级别的性能优化等

    1.1 程序的状态与命令式编程

    • 程序的状态首先包含了当前定义的全部变量
    • 有了程序的状态,我们的程序才能不断往前推进
    • 命令式编程,就是通过不断修改变量的值,来保存当前运⾏的状态,来步步推进

    1.2 函数式编程

    • 通过函数来保存程序的状态(通过函数创建新的参数和返回值来保存状态)
    • 函数一层层的叠加起来,每个函数的参数或返回值代表了⼀个中间状态
    • 命令式编程⾥一次变量值的修改,在函数式编程⾥变成了⼀个函数的转换
    • 最自然的方式:递归

    2 一等函数

    一等对象的定义:

    • 在运⾏时创建
    • 能赋值给变量或数据结构中的元素
    • 能作为参数传给函数
    • 能作为函数的返回结果

    Python 中,所有函数的都是一等对象,简称为一等函数

    2.1 高阶函数

    定义:接受函数为参数,或把函数作为返回结果的函数

    2.1.1 map 映射

    map() 是 Python 内置的高阶函数,它接收一个函数 f 和一个可迭代对象,并通过把函数 f 依次作用在 可迭代对象 的每个元素上,并返回一个新的可迭代对象。


    def f(x):
        return x * x
    
    
    print('map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])):',
          list(map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])))
    

    show:

    map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])): [1, 4, 9, 16, 25, 36, 49, 64, 81]
    

    替代方案:

    [x*x for x in range(1,10)]
    

    def format_name(s):
        s1 = s[0:1].upper() + s[1:].lower()
        return s1
    
    
    print("map(format_name, ['adam', 'LISA', 'barT']):",
          list(map(format_name, ['adam', 'LISA', 'barT'])))
    
    map(format_name, ['adam', 'LISA', 'barT']): ['Adam', 'Lisa', 'Bart']
    

    替代方案:

    [format_name(name) for i, name in enumerate(['adam', 'LISA', 'barT'])]
    

    因而,列表推导可以很好的替换 map 函数。

    2.1.2 filter 过滤器

    x = [(), [], {}, None, '', False, 0, True, 1, 2, -3]
    
    x_result = filter(bool, x) 
    list(x_result)
    
    [True, 1, 2, -3]
    

    替代方案:

    [i for i in x if bool(i)]
    

    print("filter((lambda x: x>0), range(-5, 5)):",
          list(filter((lambda x: x > 0), range(-5, 5))))
    
    filter((lambda x: x>0), range(-5, 5)): [1, 2, 3, 4]
    

    替代方案:

    [x for x in range(-5, 5) if x > 0]
    

    2.1.3 reduce 递推

    from functools import reduce
    
    m = 2
    n = 5
    reduce(lambda x, y: x * y, list(range(1, n + 1)), m)
    
    240
    

    def multiply(a, b):
        return a * b
    
    
    reduce(multiply, range(1, 5))
    
    24
    

    2.1.4 zip 并行

    print("zip( [1, 2, 3], [4, 5, 6]):", list(zip([1, 2, 3], [4, 5, 6])))
    

    show:

    zip( [1, 2, 3], [4, 5, 6]): [(1, 4), (2, 5), (3, 6)]
    

    2.1.5 sorted 排序

    >>> sorted([x * (-1) ** x for x in range(10)])
    
    [-9, -7, -5, -3, -1, 0, 2, 4, 6, 8] 
    
    >>> sorted([x * (-1) ** x for x in range(10)], reverse=True) 
    
    [8, 6, 4, 2, 0, -1, -3, -5, -7, -9] 
    
    >>> sorted([x * (-1) ** x for x in range(10)], key=abs)
    
     [0, -1, 2, -3, 4, -5, 6, -7, 8, -9]
    
    >>> sorted([x * (-1) ** x for x in range(10)], reverse=True, key=abs) 
    
    [-9, 8, -7, 6, -5, 4, -3, 2, -1, 0]
    

    minmax 同理。

    2.2 partial

    functools 这货用于高阶函数:指那些作用于函数或者返回其他函数的函数。通常情况下,只要是可以被当做函数调用的对象就是这个模块的目标。

    假设有如下函数:

    def multiply(x, y):
        return x * y
    

    现在,我们想返回某个数的双倍,即:


    >>> multiply(3, y=2)
    
    6
    
    >>> multiply(4, y=2)
    
    8
    
    >>> multiply(5, y=2)
    
    10
    

    上面的调用有点繁琐,每次都要传入 y=2,我们想到可以定义一个新的函数,把 y=2 作为默认值,即:

    def double(x, y=2):
        return multiply(x, y)
    

    现在,我们可以这样调用了:


    >>> double(3)
    
    6
    
    >>> double(4)
    
    8
    
    >>> double(5)
    
    10
    

    事实上,我们可以不用自己定义 double,利用 partial,我们可以这样:

    from functools import partial
    
    double = partial(multiply, y=2)
    

    partial 接收函数 multiply 作为参数,固定 multiply 的参数 y=2,并返回一个新的函数给 double,这跟我们自己定义 double 函数的效果是一样的。

    所以,简单而言,partial 函数的功能就是:把一个函数的某些参数给固定住,返回一个新的函数。

    需要注意的是,我们上面是固定了 multiply 的关键字参数 y=2,如果直接使用:

    double = partial(multiply, 2)
    

    2 是赋给了 multiply 最左边的参数 x


    from functools import partial
    
    def subtraction(x, y):
        return x - y
    
    f = partial(subtraction, 4)  # 4 赋给了 x
    
    >>> f(10)   # 4 - 10
    
    -6
    

    组合高阶函数:


    from functools import partial
    
    abs_sorted = partial(sorted, key=abs)
    abs_sorted([x * (-1) ** x for x in range(10)])
    

    show:

    [0, -1, 2, -3, 4, -5, 6, -7, 8, -9]
    
    abs_reverse_sorted = partial(sorted, key=abs, reverse=True)
    abs_reverse_sorted([x * (-1) ** x for x in range(10)])
    

    show:

    [-9, 8, -7, 6, -5, 4, -3, 2, -1, 0]
    

    2.3 匿名函数

    • 定义:使⽤用 lambda 表达式创建的函数,函数本身没有名字
    • 特点:只能使⽤用纯表达式,不能赋值,不能使⽤用 whiletry 等块语句
    • 语法: lambda [arg1 [,arg2 [,arg3]]]: expression

    Expressions get a value; Statements do something

    lambda & def

    写法上:

    1. def 可以用代码块,一个代码块包含多个语句
    2. lambda只能⽤单行表达式,⽽表达式仅仅是单个语句中的⼀种

    结果上:

    1. def 语句一定会增加⼀个函数名称
    2. lambda 不会,这就降低了了变量名污染的⻛险

    能用一个表达式直接放到 return 里返回的函数都可以⽤ lambda 速写

    def multiply(a, b):
        return a * b
    
    multiply_by_lambda = lambda x,y: x * y
    

    List + lambda 可以得到⾏为列表

    f_list = [lambda x: x + 1, lambda x: x ** 2, lambda x: x ** 3] 
    [f_list[j](10) for j in range(3)] 
    
    [11, 100, 1000]
    

    在 AI 领域里,这种写法常用于处理数据,比如按预定的⼀系列模式处理数据

    下面我们以两个例子来结束高阶函数:

    例1:


    L = range(6)
    
    # 计算l中每个元素的两倍和平方,并将两种组成一个列表
    # lambda表达式和python函数一样,也可以接受函数作为参数
    
    
    def twoTimes(x):
        return x * 2
    
    
    def square(x):
        return x**2
    
    
    print([list(map(lambda x: x(i), [twoTimes, square])) for i in L])
    print(list(filter(lambda x: x % 2 == 0, L)))
    
    # 内置reduce函数,计算 L 的和
    print(reduce(lambda accumValue, newValue: accumValue + newValue, L, 0))
    
    [[0, 0], [2, 1], [4, 4], [6, 9], [8, 16], [10, 25]]
    [0, 2, 4]
    15
    

    我们依然可以使用列表解析的方式替换 map & filter

    [[twoTimes(x), square(x)] for x in L]
    
    [x for x in L if x % 2 == 0]
    

    通过上面的例子我们发现,使用列表推导要比 map 与 filter 简洁且易于理解得多。

    但是,我们这里还有一个惰性计算的坑:


    f_list = [lambda x:x**i for i in range(5)]
    
    [f_list[j](3) for j in range(5)]
    
    [81, 81, 81, 81, 81]
    

    大家可以思考为什么会出现这个意想不到的结果?


    函数修饰器(Decorator)其他相关知识

    Python中的函数是对象,故函数可以被当做变量使用。

    参考资料:

    作用:

    对已有的函数添加一些小功能,却又不希望对函数内容有太多的刚性的修改。

    • 将需要添加功能的函数像普通对象一样作为参数传入修饰器在中;
    • 将函数作为修饰器的返回值返回。

    修饰器的本质: Python解释器在发现函数调用修饰器后,将其传入修饰器中,然后用返回的函数对象将自身完全替换。

    一个函数可以被多个修饰器嵌套修饰:

    @deco3
    @deco2
    @deco1
    def df():
        pass
    

    等同于 f=deco3(deco2(deco1(f)))

    示例

    添加水印 Hello World_

    def deco(func):          #修饰器deco
        def wrappedFunc():          #内嵌函数wrappedFunc(所有对于传入函数的修饰逻辑都将在此内嵌函数中实现。)
            return 'Hello World_'+func()
        return wrappedFunc
    
    # 在程序中若有函数需要修饰器修饰,只需在函数定义前使用“`@+修饰器名`”即可使用此修饰器。
    @deco
    def f():    # 调用修饰器
        return 'I am f'
    def g():     # 没有调用修饰器
        return 'I am g'
    
    print(f())
    print(g())
    
    Hello World_I am f
    I am g
    
    def deco(f):
        def g():
            return [f()[i] for i in range(5)]
        return g
    @deco
    def h():
        return [1,2,3,4,56,7,'75s']
    print(h())
    
    [1, 2, 3, 4, 56]
    
  • 相关阅读:
    Apache Dubbo已不再局限于Java语言
    生产Server遭挖矿程序入侵,暴力占用CPU
    【转载】【原创】贵在,难在,成在
    【转载】【原创】生命中,要有自己的一方晴天
    (转)网上看的一篇文章,感觉会给程序员一些启发
    (转)在JSP客户端限制表单重复提交
    (转)用javascript做删除时的提示信息
    (转)上传自动显示图片 代码
    控制文字长度,多出的文字用省略号代替
    PS快捷键 未完
  • 原文地址:https://www.cnblogs.com/q735613050/p/10073763.html
Copyright © 2020-2023  润新知