Python函数式编程(高阶函数、map/reduce、filter、sorted、匿名函数、返回函数)-3

  • 是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量;函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还返回一个函数。

一、高阶函数

  • 函数本身也可以赋值给变量,即变量指向函数。
  • 函数名也是变量。
  • 一个函数可以接受另一个函数作为参数,这种函数称之为高阶函数。
>>> def add(x, y, f): 
...     return f(x) + f(y)
... 
>>> print(add(-5, 6, abs)) 
11

1. map/reduce

  • map()函数接受两个参数,一个函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。
  • 因为Iterator是惰性序列(迭代器仅仅至某个元素时才计算),因此通过list()函数让它把整个序列都计算出来并返回一个list。
>>> def f(x):
...     return x * x
... 
>>> r = map(f, [1, 2, 3, 4, 5, 6, 7,8, 9]) 
>>> r
<map object at 0x000001C329AA5240>
>>> list(r) 
[1, 4, 9, 16, 25, 36, 49, 64, 81]
  • reduce把一个函数作用在一个序列[x1, x2, x3, x4...],这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累加。
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

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

2. filter

  • filter()函数用于过滤序列,也是两个参数,一个函数一个序列,把传入的函数依次作用于每个元素,然后根据返回值是True或False决定保留还是丢弃该元素,也是返回一个Iterator。
>>> def not_empty(s):
...     return s and s.strip
... 
>>> def not_empty(s):        
...     return s and s.strip()  
... 
>>> list(filter(not_empty, ['A', '', "c", None, '  ']))
['A', 'c']
>>>

3. sorted

  • Python内置的sorted()函数就可以对list进行排序。
  • 同时也是一个高阶函数,它可以接受一个key函数来实现自定义的排序,例如按照绝对值大小排序。
  • 默认是从小到大进行排序,如果想要排序相反,则传入第三个参数reverse=True
>>> sorted([36, 5, -12, 9, -21]) 
[-21, -12, 5, 9, 36]
>>> sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]
>>> sorted(['bob', 'about', 'Zoo', 'Credit'])
['Credit', 'Zoo', 'about', 'bob']
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
['about', 'bob', 'Credit', 'Zoo']
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
['Zoo', 'Credit', 'bob', 'about']

二、返回函数

  1. 高阶函数除了接受函数作为参数外,还使用函数作为结果返回,但是该函数没有执行。
  2. 我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。
  3. 当我们调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数。
  4. 返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

三、匿名函数

  1. 关键字lambda表示匿名函数,冒号前面的x表示函数参数
  2. 匿名函数有个限制,就是只能够有一个表达式,不能写return,返回值就是该表达式的结果。
  3. 不用担心函数名冲突,同时也是一个函数对象,也可将其赋给变量,然后调用。
  4. 也可以作为函数的返回值。

四、装饰器

  1. 函数也是夜歌对象,而且函数对象可以赋值给变量,所以通过变量也能够调用函数。
  2. 函数对象有一个__name__属性,可以拿到函数的名字。
  3. 假设我们要增强now()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)
  4. 把@log放到now()处,相当于执行了语句now = log(now)
  5. 在面向对象(OOP)的设计模式中,decorator被称为装饰模式。
def log(func):
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper

@log
def now():
    print('2015-3-25')
    
>>> now()
call now():
2015-3-25

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')

>>> now()
execute now():
2015-3-25

import functools

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

五、偏函数

  1. functools.partial就是帮助我们创建一个偏函数的,不需要我们自己定义int2(), 可以直接使用下面的代码创建一个新函数int2:
  2. 简单总结functools.partial作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85

>>> int2('1000000', base=10)
1000000
posted @ 2020-03-26 21:53  睿晞  阅读(155)  评论(0编辑  收藏  举报