Python函数式编程

Posted gzhjj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python函数式编程相关的知识,希望对你有一定的参考价值。

函数是Python内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计。函数就是面向过程的程序设计的基本单元。
越低级的语言,越贴近计算机,抽象程度低,执行效率高;越高级的语言,越贴近计算,抽象程度高,执行效率低。
函数式编程就是一种抽象程度很高的编程范式。
函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!

1. 高阶函数

函数本身可以赋值给变量,即变量可以指向函数。如果一个变量指向了一个函数,可以通过该变量来调用这个函数。函数名其实就是指向函数的变量。
一个函数接收另一个函数作为参数,这种函数就称之为高阶函数

>>> def add(x, y, f):
        return f(x) + f(y)

>>> print(add(-5, 6, abs)) # abs为Python内置的求绝对值的函数
11

编写高阶函数,就是让函数的参数能够接收别的函数。

1.1 map

>>> def f(x):
        return x * x

>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9]) # map(function, Iterable)
>>> list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]

map()接收两个参数,第一个是函数,第二个是Iterable
map()将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。

1.2 reduce

reduce把函数作用在序列上,这个函数必须接收两个参数,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
# 把字符串转化为数字
from functools import reduce

DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}

def str2int(s):
    def fn(x, y):
        return x * 10 + y
    def char2num(s):
        return DIGITS[s]
    return reduce(fn, map(char2num, s))
    
>>> from test import str2int
>>> str2int('13579')
13579
from functools import reduce

DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}

def char2num(s):
    return DIGITS[s]

def str2int(s):
    return reduce(lambda x, y: x * 10 + y, map(char2num, s)) # 使用lambda函数简化

lambda x, y: x * 10 + y就是lambda函数。

1.3 filter

Python内建的filter()函数用于过滤序列。
filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。

>>> def is_odd(n):
    return n % 2 == 1

>>> list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
[1, 5, 9, 15]

注意到filter()函数返回的是一个Iterator,也就是一个惰性序列,所以要强迫filter()完成计算结果,需要用list()函数获得所有结果并返回list

1.4 sorted

Python内置的sorted()函数就可以对list进行排序。

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

默认情况下,对字符串排序,是按照ASCII的大小比较的。

2. 返回函数

def lazy_sum(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum
    
>>> from test import lazy_sum
>>> f = lazy_sum(1,3,5,7,9)
>>> f
<function lazy_sum.<locals>.sum at 0x0093C6F0>
>>> f()
25

3. 匿名函数

>>> list(map(lambda x: x * x, [1, 2, 3, 4, 5]))
[1, 4, 9, 16, 25]

关键字lambda表示匿名函数,冒号前面的x表示函数参数。
匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。

4. 装饰器

由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。
函数对象有一个__name__属性,可以拿到函数的名字。

>>> def hello():
    print('Hello!')

    
>>> h = hello
>>> h()
Hello!
>>> h.__name__
'hello'
>>> hello.__name__
'hello'

在代码运行期间动态增加功能的方式,称之为装饰器

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

def log(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper
"""
    
@log
def now():
    print('2019-4-7')


now()

# call now():
# 2019-4-7

如果装饰器本身需要传入参数,那就需要编写一个返回装饰器的高阶函数。

def log(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator
"""
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
""" 
    
@log('execute')
def now():
    print('2019-4-7')
    

now()

# execute now():
# 2019-4-7

5. 偏函数

当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。

import functools

int2 = functools.partial(int, base=2)
print(int2('1000000')) # int('1000000', base=2)

"""
def int2(x, base=2):
    return int(x, base)
    
"""

# 64

参考资料:

以上是关于Python函数式编程的主要内容,如果未能解决你的问题,请参考以下文章

写 Python 代码不可不知的函数式编程技术

函数式编程/命令式编程

python_函数式编程

《On Java 8》中文版 第十三章 函数式编程

python之函数式编程

Python进阶学习——函数式编程