python 函数式编程

1. 高阶函数

函数本身可以做为变量赋值,如

f = abs

这时候调用被赋值的函数变量,与函数的调用结果是相同的

num = f(-10)
>>> 10

函数名本身是一个变量,且可以作为另一个函数的参数

def add(x, y, f):
    return f(x) + f(y)
num = add(-5, 8, abs)

讲abs函数作为参数传入另一个函数中。
学习了两个内建函数

  • map() 函数
    map函数表示将函数作用于列表的每一个元素上
def f(x)
    return x *x
r = map(f, list(range(1, 10)))
r = map(str, list(range(1,10)))

注意:生成的是一个新的惰性序列,当调用next时才计算出下一个元素

  • reduce 函数
    reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
from functools import reduce
def reAdd(x, y): 
    return x + y
r = reduce(reAdd, list(range(1, 10)))

计算序列的累加
注:reduce 已经被移至functools

题:把str转换为int的函数

def fn(x, y):
    return x * 10 + y
def char2num(s):
    return ord(s) - ord('0')
reduce(fn, map(char2num, '13579'))
>>> 13578

练习:

利用map()函数,把用户输入的不规范的英文名字,变为首字母大写,其他小写的规范名字。输入:['adam', 'LISA', 'barT'],输出:['Adam', 'Lisa', 'Bart']:

def normalize(name):
    L1, L2 = [], []
    if len(name) > 0:
        L1 = name[:1]
    if len(name) > 1:
        L2 = name[1:]
    L1 = L1.upper()
    L2 = L2.lower()
    return L1 + L2

L1 = ['adam', 'LISA', 'barT']
L2 = list(map(normalize, L1))
['Adam', 'Lisa', 'Bart']

练习:

Python提供的sum()函数可以接受一个list并求和,请编写一个prod()函数,可以接受一个list并利用reduce()求积:

def prod(L):
    return reduce(lambda x, y : x * y, L)

>>> prod([3, 5, 7, 9])
945

练习:

利用map和reduce编写一个str2float函数,把字符串'123.456'转换成浮点数123.456:

def str2float(s):
    if len(s) > 0: 
        i = 0
        L1 = s[:s.find('.')]
        L2 = s[:s.find('.'):-1] # 用这种方式取逆序,list。find() 寻找元素下标
        # L2 = list(s[s.find('.') + 1:])
        # L2.reverse()
        def fn2ten(x, y):   
            return x * 10 + y
        def fn2one(x, y):
            return x * 0.1 + y
    else:
        return 0
    return reduce(fn2ten, map(lambda x: ord(x) - ord('0'), L1)) + reduce(fn2one, map(lambda x: ord(x) - ord('0'), L2)) / 10 # ord()函数,取字符串对应阿斯克码
  • filter() 函数
    传入返回值为布尔值的函数,用于过滤列表
def is_odd(n):
    return n % 2 == 0
print(list(filter(is_odd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])))
[2, 4, 6, 8, 10]

注意到filter()函数返回的是一个Iterator,也就是一个惰性序列

用filter求素数

计算素数的一个方法是埃氏筛法,它的算法理解起来非常简单:

  1. 首先,列出从2开始的所有自然数,构造一个序列
  2. 取序列的第一个数2,它一定是素数,然后用2把序列的2的倍数筛掉
  3. 取新序列的第一个数3,它一定是素数,然后用3把序列的3的倍数筛掉
    ...
  4. 不断筛下去,就可以得到所有的素数
# 构造一个从3开始的奇数序列(剔除2的倍数,直接从第三步开始)
def _odd_iter():
    n = 1
    while True:
        n += 2
        yield n

# 然后定义一个筛选函数:
def _not_divisible(n):
    return lambda x: x % n > 0

# 最后,定义一个生成器,不断返回下一个素数:
def primes():
    yield 2
    it = _odd_iter() # 生成3开始的奇数序列
    while True:
        n = next(it) # 得到3
        yield n # 返回3
        it = filter(_not_divisible(n), it) # 筛除3的倍数,然后生成一个从5开始的序列...

练习

回数是指从左向右读和从右向左读都是一样的数,例如12321,909。请利用filter()筛选出回数:

def _all_nums():
    n = 0
    while True:
        yield n
        n += 1

def num2char(n):
    return str(n)

def is_palindrome(n):
    s = num2char(n)
    mid = len(s) // 2 - 1
    n = 0
    while n <= mid:
        if s[n] == s[-n - 1]:
            n += 1
        else:
            return False        
    return True

filter(is_palindrome, range(1, 1000)) # 1~1000的回文

小结
filter()的作用是从一个序列中筛出符合条件的元素。由于filter()使用了惰性计算,所以只有在取filter()结果的时候,才会真正筛选并每次返回下一个筛出的元素。

  • sorted()函数
    语法:sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
    sorted(list, options, reverse) 1.列表 2.对元素的操作,返回排序元素 3.是否逆序输出
    sorted()也是一个高阶函数。用sorted()排序的关键在于实现一个映射函数。

练习

假设我们用一组tuple表示学生名字和成绩:
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
按姓名排序

def by_name(t): 
    t2 = t[0].lower()
    return t2 # 返回要排序的部分

L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
L2 = sorted(L, key=by_name)

再按成绩从高到低排序

def by_score(t):
    t2 = t[1]
    return t2
L2 = sorted(L, key=by_score, reverse=True) # 从高到低需要逆序输出

2. 返回函数

函数作为返回值

  • 闭包
    !!! 返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

练习

利用闭包返回一个计数器函数,每次调用它返回递增整数

def createCounter():
    global n # 定义一个全局变量
    n = 0
    def counter():
        global n
        n += 1
        return n
    return counter

3. 匿名函数

匿名函数lambda x: x * x实际上就是:

def f(x):
    return x * x

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

练习

请用匿名函数改造下面的代码:

def is_odd(n):
    return n % 2 == 1
L = list(filter(is_odd, range(1, 20)))
# 改造后
L = list(filter(lambda x: x % 2 == 1, range(1, 20)))

4. 装饰器

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

练习

请设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间

import functools, time
def metric(func):
    @functools.wraps(func) # 不改变原函数的名称
    def wrapper(*args, **kw):
        print('%s executed in %s ms' % (func.__name__, time.ctime()))
        f = func(*args, **kw)
        return f
    return wrapper

使用方法,在函数的定义时加上装饰器函数,用@

@metric
def fast(x, y):
    time.sleep(0.0012)
    return x + y

练习

请编写一个decorator,能在函数调用的前后打印出'begin call'和'end call'的日志。支持:传参和不传参

def log(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            if isinstance(text, str):
                print(text)
            print('begin call')
            func(*args, **kw)
            print('end call')
        return wrapper

    if isinstance(text, str):
        return decorator
    else:
        return decorator(text)

5. 偏函数

当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。
比如,将int函数改为接受二进制源

# 原本
int('10010', 2)
# 修改
int2 = functools.partial(int, base = 2)
int2('10010')

>>> int('10010', 2)
18
>>> int2('10010')
18

你可能感兴趣的:(python 函数式编程)