• 第四章-函数式编程


    函数式编程(Functional Programming), 可以归结为面向过程的程序设计, 但是其思想更加接近于数学计算

    函数有副作用: 对于同一个函数相同的输入, 产生的结果是不一样的

    函数无副作用: 上述的前提下函数输出是一样的

    函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量

    函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数

    1 高阶函数

      变量可以指向函数, 也就是函数本身也可以复制给变量

      实际上函数名本身也是变量

      将函数作为参数传给另一个函数, 这样的函数就是高阶函数

    1.1 map和reduce

      map有两个参数, 一个是函数, 另一个是迭代器

    >>> def f(x):
    ...     return x * x
    ...
    >>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
    >>> list(r)
    [1, 4, 9, 16, 25, 36, 49, 64, 81]
    

      reduce把一个函数作用在一个序列上, 这个函数必须接受两个参数,  reduce会先把序列的前两个参数传入函数计算,返回的结果会继续和下一个参数继续进行运算,直到完成整个序列的运算

    >>> from functools import reduce
    >>> def add(x, y):
    ...     return x + y
    ...
    >>> reduce(add, [1, 3, 5, 7, 9])
    25
    

      其效果等同于1+3+5+7+9

    >>> from functools import reduce
    >>> def fn(x, y):
    ...     return x * 10 + y
    ...
    >>> def char2num(s):
    ...     return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
    ...
    >>> reduce(fn, map(char2num, '13579'))
    13579
    

      该程序可以整个为

    from functools import reduce
    
    def str2int(s):
        def fn(x, y):
            return x * 10 + y
        def char2num(s):
            return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
        return reduce(fn, map(char2num, s))
    

      最后还可以用lambda函数优化

    from functools import reduce
    
    def char2num(s):
        return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
    
    def str2int(s):
        return reduce(lambda x, y: x * 10 + y, map(char2num, s))
    

    1.2 filter

      filter用于过滤, 会传入两个参数, 一个是函数, 一个是序列. 函数只会处理一个参数且函数返回值是True或者False.当返回值是True的时候该值保留下来, 当返回值是False的时候该参数将不会被保留.

    def not_empty(s):
        return s and s.strip()
    
    list(filter(not_empty, ['A', '', 'B', None, 'C', '  ']))
    # 结果: ['A', 'B', 'C']
    

    1.3 sorted

      sorted是用于排序的.

      当排序的序列中是数字的时候, 直接对数字进行排序. 当序列中的内容是非数字的时候排序就不能以大小判断了. 此时可以传入一个参数key来限定排序方法.

    >>> sorted([36, 5, -12, 9, -21], key=abs)
    [5, 9, -12, -21, 36]
    

      默认情况下, 字母的排序是按照ASCII的排序关系排序

      要进行反向排序, 需要加入第三个参数, reverse=True

    >>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
    ['about', 'bob', 'Credit', 'Zoo']
    

    2 返回函数

      函数可以做为对象赋值给变量, 因而返回函数实际上就是将函数封装传出去了

      例如想要做一个求和的函数, 但是并不希望调用该函数执行之后就返回结果, 而是在想要使用的时候调用.

    def lazy_sum(*args):
        def sum():
            ax = 0
            for n in args:
                ax = ax + n
            return ax
        return sum
    

      可以看到 lazy_sum()函数返回的就是sum函数.当返回值执行函数的时候才进行计算

    >>> f = lazy_sum(1, 3, 5, 7, 9)
    >>> f
    <function lazy_sum.<locals>.sum at 0x101c6ed90>
    >>> f()
    25
    

    3 闭包

      闭包(Closure)就是传出返回函数, 传出的函数在执行的时候可以调用原函数内容的变量. 类似于上述lazy_sun的情况.

      闭包的基本形式是:

        在函数F1中, 定义F2, F2只能引用F1定义的变量, 之后F1函数返回F2的函数名字

    def count():
        fs = []
        for i in range(1, 4):
            def f():
                 return i*i
            fs.append(f)
        return fs
    
    f1, f2, f3 = count()
    

      此时关键来了, 当执行f1, f2, f3的时候返回的结果是..

    >>> f1()
    9
    >>> f2()
    9
    >>> f3()
    9
    

      原因是由于闭包的特性是在具体调用的时候去取原函数中变量的值. 然而在返回fs的时候会走count()中的代码, 在具体函数执行之前i值已经是i=3了.

      因此在闭包的时候, 不能引用任何循环变量.

      如果真的需要引用这个循环变量的话, 就需要将循环变量绑定到一个函数的参数当中.

    def count():
        def f(j):
            def g():
                return j*j
            return g
        fs = []
        for i in range(1, 4):
            fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
        return fs
    

      具体还可以通过lambda表达式优化上述代码

    def count():
        def f(j):
            def g():
                return j*j
            return g
        return [f(i) for i in range(1,4)]
    
    f1, f2, f3 = count()

    4 匿名函数

      匿名函数用于一些操作不必要比较短小没必要写成函数的方式

    >>> list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
    [1, 4, 9, 16, 25, 36, 49, 64, 81]
    

      匿名函数只能是一个表达式, 且不用写return, 返回值就是表达式的结果.

      常用的匿名函数用lambda表达式.

      写法:

        lambda 参数 : 参数的运算式  (返回的结果是参数的表达式的结果)

        lambda 参数表达式  (返回的值就是表达式的计算结果)

    5 装饰器

      装饰器(Decorator), 在不修改原函数的情况下, 在代码运行期间动态增加功能的方式

    def log(func):
        def wrapper(*args, **kw):
            print('call %s():' % func.__name__)
            return func(*args, **kw)
        return wrapper
    @log
    def now():
        print('2017-3-28')
    
    >>> now()
    call now():
    2017-3-28
    

      此处的@log相当于执行了

    now = log(now)
    

      另外定义装饰器的时候, 可以用 @装饰器(参数) 装饰器上添加参数传入, 对此原有的装饰器需要在外层再加一层函数来传入参数, 因为如果只在刚才的最外层添加是无法做到准确传入传入的参数的

    def log(text):
        def decorator(func):
            def wrapper(*args, **kw):
                print('%s %s():' % (text, func.__name__))
                return func(*args, **kw)
            return wrapper
        return decorator
    
    @log('execute')
    def now():
        print('2015-3-25')
    

      此处的@log('execute')相当于执行了

    now = log('execute')(now)

      但是这样的装饰器还是有一个问题就是:

        每个函数对象都有一个__name__属性, 这个属性保存了函数的名字. 但是执行完装饰器之后, 该属性值__name__会被覆盖成别的名字.改进方法是:

    import functools
    
    def log(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print('call %s():' % func.__name__)
            return func(*args, **kw)
        return wrapper
    

        对于带参数的装饰器有:

    import functools
    
    def log(text):
        def decorator(func):
            @functools.wraps(func)
            def wrapper(*args, **kw):
                print('%s %s():' % (text, func.__name__))
                return func(*args, **kw)
            return wrapper
        return decorator

    6 偏函数

      偏函数是 functools.partial模块里, 通过固定某个函数的某个参数来达到形成新函数简化操作的目的.

      通过functools.partial可以传入原来的函数名字, 设定的默认参数的值, 来返回一个新的函数更加方便的定义并且使用

      诸如:

        int(要转化的参数, base进制)

        当我们要定义直接转化二进制的时候可以

    int2 = functools.partial(int, base=2)
    

      具体的

    >>> import functools
    >>> int2 = functools.partial(int, base=2)
    >>> int2('1000000')
    64
    >>> int2('1010101')
    85

      

    人若有恒 无所不成
  • 相关阅读:
    process crashed and the client did not handle it, not reloading the page because we reached the maximum number of attempts
    mac 查看ip
    axios和vue-axios的关系
    export default 和 export 区别
    Mac 编辑hosts文件
    npm --save-dev --save 的区别
    CommonHelper 公共类
    2.06StuModify.aspx(修改姓名,性别,所在班级)
    linux网桥浅析
    Bridge的数据在内核处理流程
  • 原文地址:https://www.cnblogs.com/weihuchao/p/6628413.html
Copyright © 2020-2023  润新知