函数是Python内建支持的一种封装,通过把大段代码拆成函数,通过一层一层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计。函数就是面向过程的程序设计的基本单元。
函数式编程
- 函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。
- 函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数。
- Python对函数式编程提供部分支持。由于Python允许使用变量,因此,Python不是纯函数式编程语言。
一、高阶函数
高阶函数(Higher-order function):
函数本身也可以赋值给变量,即:变量可以指向函数。
f = abs abs(-10) f(-10)
函数名也是变量,函数名其实就是指向函数的变量。
既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
def add(x, y, f): return f(x) + f(y) a = add(-5, 6, abs) # 参数x,y和f分别接收-5,6和abs print(a) # 11
map()
函数接收两个参数,一个是函数,一个是Iterable
,map
将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator
返回。def f(x): return x * x r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9]) list(r) # map()传入的第一个参数是f,即函数对象本身。由于结果r是一个Iterator,Iterator是惰性序列,因此通过list()函数让它把整个序列都计算出来并返回一个list。 L = [] for n in [1, 2, 3, 4, 5, 6, 7, 8, 9]: L.append(f(n)) print(L) # map()作为高阶函数,事实上它把运算规则抽象了 # 计算任意复杂的函数,比如,把这个list所有数字转为字符串: list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])) # ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']
reduce
把一个函数作用在一个序列[x1,x2,x3,…]上,这个函数必须接收两个参数,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]) # 求和可以直接用sum() # 把序列[1, 3, 5, 7, 9]变换成整数13579 def fn(x, y): return x * 10 + y reduce(fn, [1,3,5,7,9]) # 把str转换为int的函数 def char2num(s): digits = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9} return digits[s] reduce(fn, map(char2num, '246')) # 整理成一个str2int的函数 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)) str2int('13579') # 用lambda函数进一步简化 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))
filter()
函数用于过滤序列,接收一个函数和一个序列,filter()
把传入的函数依次作用于每个元素,然后根据返回值是True
还是False
决定保留还是丢弃该元素。# 1.在一个list中,删掉偶数,只保留奇数。 def is_odd(n): return n % 2 == 1 list(filter(is_odd, [1,2,3,4,5,6,7,8,9])) # [1, 3, 5, 7, 9] # 2.删除一个序列中的空字符 def not_empty(s): return s and s.strip() list(filter(not_empty, ['A', '', 'b', 'C', None, ' '])) # ['A', 'b', 'C'] # strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列。 str1.strip('h') #删除str1中首尾的h
用
埃氏筛法
求素数。埃拉托斯特尼筛法:要得到自然数n以内的全部素数,必须把不大于根号n的所有素数的倍数剔除,剩下的就是素数。def main(): for n in primes(): if n < 1000: print(n) else: break # 输出1000以内的素数 def _odd_iter(): #构造从3开始的奇数序列 n = 1 while True: n = 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) #这个生成器先返回第一个素数2,然后,利用filter()不断产生筛选后的新的序列 if __name__ == '__main__' : main()
filter()
的作用是从一个序列中筛出符合条件的元素。sorted()函数
排序算法,是一个高阶函数,关键在于实现一个映射函数。它还可以接收一个key
函数来实现自定义的排序。key指定的函数将作用于list的每一个元素上,并根据key函数返回的结果进行排序。要进行反向排序,不必改动key函数,可以传入第三个参数
reverse=True
。sorted([36,15,2,48,25]) sorted([36,15,2,-48,25,-29], key=abs) # 按绝对值大小排序 sorted(['NUS', 'GLY', 'about', 'Dau','bob']) #字符串排序,依次比较ASCII码值 sorted(['NUS', 'GLY', 'about', 'Dau','bob'], key=str.lower) # 排序忽略大小写,可将字符串都改为大写或小写 sorted(['NUS', 'GLY', 'about', 'Dau','bob'], key=str.lower, reverse=True) # 反向排序 from operator import itemgetter students = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)] print(sorted(students, key=itemgetter(0))) print(sorted(students, key=lambda t: t[1])) print(sorted(students, key=itemgetter(1), reverse=True))
二、返回函数
函数作为返回值,高阶函数可以接收函数作为参数,也可以把函数作为一个返回值。
def sum(*args): def add(): s = 0 for i in args: s = s + i return s return add f = sum(2,4,6,8,10) print(f()) # 30
内部函数可以引用外部函数的参数和局部变量,上例中sum返回函数add时,相关参数和变量都保存在返回函数中。
f1 = sum(2,4,6,8,10) print(f1 == f) # False # 每次调用都会返回一个新的函数,即使传入相同的参数,f1()和f()的调用结果互不影响。
闭包
:在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个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9
若要引用循环变量,再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变。
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 f1, f2, f3 = count() print(f1()) # 1 print(f2()) # 4 print(f3()) # 9 缺点是代码较长,可利用lambda函数缩短代码。
使用闭包时,内层函数对外层变量赋值前,需要先使用nonlocal声明该变量不是当前函数的局部变量。
def inc(): #局部函数fn()和局部函数引用到的函数外的x形成一个整体,构成了闭包 x = 0 def fn(): nonlocal x x = x + 1 return x return fn f = inc() print(f()) # 1 print(f()) # 2
闭包的记忆功能,在 Python 中,被捕获到闭包中的变量让闭包本身拥有了记忆效应,闭包中的逻辑可以修改闭包捕获的变量,变量会跟随闭包生命期一直存在,闭包本身就如同变量一样拥有了记忆功能。
闭包就是一个函数和与其相关的引用环境组合的一个整体。
三、匿名函数
- 关键字
lambda
表示匿名函数,冒号前面的x
表示函数参数。 - 不必担心函数名冲突。
- 在传入函数时,有时不需要显式地定义函数,直接传入匿名函数更方便。
四、装饰器
- 在代码运行期间动态增加功能的方式,称之为装饰器(Decorator)。
- 在面向对象(OOP)的设计模式中,decorator被称为装饰模式。OOP的装饰模式需要通过继承和组合来实现,而Python除了能支持OOP的decorator外,直接从语法层次支持decorator。Python的decorator可以用函数实现,也可以用类实现。
- decorator可以增强函数的功能,定义起来虽然有点复杂,但使用起来非常灵活和方便。
五、偏函数
偏函数(Partial function):通过设定参数的默认值,可以降低函数调用的难度。
functools.partial
创建一个偏函数。import functools int2 = functools.partial(int, base=2) int2('10101100') int16 = functools.partial(int, base=16) int16('20')
当函数的参数个数太多,需要简化时,使用
functools.partial
可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。