💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[toc] # 函数式编程 ## 高阶函数 ### 特点 1. 变量可以指向函数 2. 函数名也是变量 3. 参数可以传入函数 ```python def add(x, y, f): return f(x) + f(y) print(add(-5, 6, abs)) # 11 ``` ### 常用的高阶函数 #### map > 接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。 ```python def f(x): return x*x r = map(f,[1,2,3,4,5,6,7,8]) # <map object at 0x0000023D81A60FD0> list(r) # [1, 4, 9, 16, 25, 36, 49, 64] list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])) # ['1', '2', '3', '4', '5', '6', '7', '8', '9'] ``` #### reduce > reduce把一个函数作用在一个序列``[x1, x2, x3, ...]``上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是 ``` reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4) ``` ```python from functools import reduce # add只能是是两个参数 def add(x,y): return x+y # 当然也可以是可变参数 def add(*numbers): sum = 0 for n in numbers: sum += n return sum reduce(add,[1,2,3,4,5]) # 1+2+3+4+5=15 def fn(x, y): return x * 10 + y reduce(fn, [1, 3, 5, 7, 9]) # 函数式编程实现字符串转int DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9} def str2int(str): def fn(x,y): return x*10+y def char2num(i): return DIGITS[i] return reduce(fn,map(char2num,str)) str2int('2878') # 2878 # 通过lambda函数进一步简化 def char2num(s): return DIGITS[s] def str2int(s): return reduce(lambda x, y: x * 10 + y, map(char2num, s)) ``` #### filter > 和map()类似,filter()也接收一个函数和一个序列(包括无限的惰性序列)。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。 ```python # 删掉偶数,只保留奇数 def is_odd(n): return n % 2 == 1 list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])) # 结果: [1, 5, 9, 15] # 空字符串删掉 def not_empty(s): return s and s.strip() list(filter(not_empty, ['A', '', 'B', None, 'C', ' '])) # ['A', 'B', 'C'] ``` **用Python来实现埃氏筛法** 埃氏筛法:取序列的第一个素数,筛选掉其倍数,返回新的序列,再取第一个素数,再筛选掉其倍数,再返回新的序列 ```python # 构建一个从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() while True: n = next(it) yield n # it = filter(_not_divisible(n),it) # 注意这里的n,n是上文的n,it里面的元素传入_not_divisible里面的x it = filter(lambda x,n = n : x % n > 0, it) for num in primes(): if num < 100: print(num) else: pass ``` > xxx 这个地方还是有点不太好理解 #### sorted ```python sorted([36, 5, -12, 9, -21]) # [-21, -12, 5, 9, 36] 默认从小到大 sorted([36, 5, -12, 9, -21], key=abs) # 按绝对值大小排序,key指定的函数将作用于list的每一个元素上,并根据key函数返回的结果进行排序。 sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower) # 忽略大小写的排序,因为字符串默认是按照ASCII的大小比较的 'Z' < 'a' sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True) # reverse可以指定进行反向排序 # 按成绩从大到小排序 L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)] sorted(L,key=lambda t : t[1],reverse=True) # [('Adam', 92), ('Lisa', 88), ('Bob', 75), ('Bart', 66)] ``` ## 函数作为返回值 ### 实现函数懒加载 ```python def lazy_sum(*args): # 里面的变量全部被闭包了 def sum(num): ax = 0 for n in args: ax += n ax *= num return ax return sum # 注意,内存地址不一样,表示每次返回的都是一个新的函数 func = lazy_sum(1,2,3,4,5) # <function lazy_sum.<locals>.sum at 0x00000176841D5510> func2 = lazy_sum(1,2,3,4,5) # <function lazy_sum.<locals>.sum at 0x00000236D9D4A7B8> func(10) # 15 实现函数懒加载,此处的传参是传给内部函数 ``` ### 闭包 ```python 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都是9,因为执行的时候i都变成3了 def count(): fs = [] for i in range(1, 4): def f(i): return i*i fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f() return fs f1, f2, f3 = count() # f1 = 1,f2 = 4,f3 = 9 ``` ### 示列 #### 计数器 ```python # example 1: def counter(): n = [0] def compute(): n[0] += 1 return n[0] return compute # example 2: def counter(): n = 0 def compute(): # n = n + 1 # n为标量(数值,字符串,浮点数),在compute中被修改,就被认为是compute内部的变量,而不是外部的变量,此时就会报错 y = n + 1 # 更改为y就没事,但实现不了 return y return compute ``` ## 匿名函数 lambda ```python list(map(lambda x : x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])) # lambda x : x * x 能写在一行的函数,冒号前面的表示参数 def build(x, y): return lambda: x * x + y * y ``` ## 装饰器 Python一切皆对象,有对象就有属性 ```python def now(): print('2018-2-27 13:40:26') print(now) # 函数名也是变量名 <function now at 0x0000016D2DBE2E18> func = now # 赋值给另外一个变量以后,还是对同一个内存进行引用 print(func) # <function now at 0x0000016D2DBE2E18> print(func.__name__) # now ``` 现在,假设我们要增强now()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。 ```python def log(func): def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper # 通过@语法把decorator置于函数的定义处,相当于now = log(now) @log def now(): print('2015-3-25') now() # 此处执行的是wrapper ``` **装饰器有参数** ```python 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() # 最终执行的是log('execute')(now) => decorator(now) => wrapper(*args, **kw); now.__name__ # 使用装饰器后的__name__已经变成wrapper了,所以,需要把原始函数的__name__等属性复制到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 @log('execute') def now(): print('2015-3-25') r = now.__name__ # now ``` ## 偏函数 用于固定已有的函数参数,简化参数传入 ```python # 新建一个函数来固定参数 def int2(x, base=2): return int(x, base) # 通过functools.partial来固定参数 import functools int2 = functools.partial(int, base=2) int2('1000000', base=10) # 固定后一样可以传入新的 # 相当于 kw = { 'base': 2 } int('10010', **kw) max2 = functools.partial(max, 10) max2(5, 6, 7) # 相当于 args = (10, 5, 6, 7) max(*args) ```