本文共 6145 字,大约阅读时间需要 20 分钟。
本文参考:
函数本身也可以赋值给变量,即:变量可以指向函数
>>> f = abs>>> f
变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数
def add(x, y, f): return f(x) + f(y)
map()
函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回
>>> def f(x):... return x * x...>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])>>> list(r)[1, 4, 9, 16, 25, 36, 49, 64, 81]
r是一个Iterator
,Iterator
是惰性序列,因此通过list()
函数让它把整个序列都计算出来并返回一个list
reduce()
把一个函数作用在一个序列[x1, x2, x3, …]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
把序列[1, 3, 5, 7, 9]变换成整数13579:
>>> from functools import reduce>>> def fn(x, y):... return x * 10 + y...>>> reduce(fn, [1, 3, 5, 7, 9])13579
配合map()
,我们就可以写出把str
转换为int
的函数:
from functools import reduceDIGITS = { '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()
也接收一个函数和一个序列,然后根据返回值是True
还是False
决定保留还是丢弃该元素
把一个序列中的空字符串删掉:
def not_empty(s): return s and s.strip()list(filter(not_empty, ['A', '', 'B', None, 'C', ' ']))# 结果: ['A', 'B', 'C']
用filter求素数:
计算素数的一个方法是埃氏筛法,它的算法理解起来非常简单:
首先,列出从2开始的所有自然数,构造一个序列:
2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, …取序列的第一个数2,它一定是素数,然后用2把序列的2的倍数筛掉:
3,取新序列的第一个数3,它一定是素数,然后用3把序列的3的倍数筛掉:
5,取新序列的第一个数5,然后用5把序列的5的倍数筛掉:
7, 8 , 9 , 10 , 11, 12 , 13, 14 , 15 , 16 , 17, 18 , 19, 20 , …
不断筛下去,就可以得到所有的素数
用Python来实现这个算法,可以先构造一个从3开始的奇数序列:
def _odd_iter(): 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()不断产生筛选后的新的序列。
由于primes()也是一个无限序列,所以调用时需要设置一个退出循环的条件:
# 打印1000以内的素数:for n in primes(): if n < 1000: print(n) else: break
Iterator
是惰性计算的序列,所以我们可以用Python表示“全体自然数”,“全体素数”这样的序列
sorted()
函数可以对list进行排序:
>>> sorted([36, 5, -12, 9, -21])[-21, -12, 5, 9, 36]
sorted()
函数也是一个高阶函数,它还可以接收一个key
函数来实现自定义的排序,key指定的函数将作用于list
的每一个元素上,并根据key函数返回的结果进行排序
>>> sorted([36, 5, -12, 9, -21], key=abs)[5, 9, -12, -21, 36]
对比原始的list
和经过key=abs
处理过的list
list = [36, 5, -12, 9, -21]keys = [36, 5, 12, 9, 21]
字符串排序的例子:
>>> sorted(['bob', 'about', 'Zoo', 'Credit'])['Credit', 'Zoo', 'about', 'bob']
实现忽略大小写的排序:
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)['about', 'bob', 'Credit', 'Zoo']
反向排序,可以传入第三个参数reverse=True
:
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)['Zoo', 'Credit', 'bob', 'about']
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。
一个可变参数的求和计算,如果不需要立刻求和,而是在后面的代码中,根据需要再计算怎么办?可以不返回求和的结果,而是返回求和的函数:
def lazy_sum(*args): def sum(): ax = 0 for n in args: ax = ax + n return ax return sum
>>> f = lazy_sum(1, 3, 5, 7, 9)>>> f.sum at 0x101c6ed90>
调用函数f时,才真正计算求和的结果:
>>> f()25
我们在函数lazy_sum
中又定义了函数sum
,并且,内部函数sum
可以引用外部函数lazy_sum
的参数和局部变量,当lazy_sum
返回函数sum
时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。
我们来看一个例子:
def count(): fs = [] for i in range(1, 4): def f(): return i*i fs.append(f) return fsf1, f2, f3 = count()
你可能认为调用f1()
,f2()
和f3()
结果应该是1,4,9,但实际结果是:
>>> f1()9>>> f2()9>>> f3()9
全部都是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
lambda
表示匿名函数,冒号前面的x
表示函数参数
匿名函数lambda x: x * x
实际上就是:
def f(x): return x * x
匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量
>>> f = lambda x: x * x>>> fat 0x101c6ef28>>>> f(5)25
在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)
定义一个能打印日志的decorator,可以定义如下:
def log(func): def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper
观察上面的log
,因为它是一个decorator,所以接受一个函数作为参数,并返回一个函数。我们要借助Python的@语法,把decorator置于函数的定义处:
@logdef now(): print('2015-3-25')
调用now()
函数,不仅会运行now()
函数本身,还会在运行now()
函数前打印一行日志:
>>> now()call now():2015-3-25
把@log
放到now()
函数的定义处,相当于执行了语句:
now = log(now)
由于log()
是一个decorator,返回一个函数,所以,原来的now()
函数仍然存在,只是现在同名的now
变量指向了新的函数,于是调用now()
将执行新函数,即在log()
函数中返回的wrapper()
函数。
要自定义log的文本:
def log(text): def decorator(func): def wrapper(*args, **kw): print('%s %s():' % (text, func.__name__)) return func(*args, **kw) return wrapper return decorator
3层嵌套的decorator用法如下:
@log('execute')def now(): print('2015-3-25')
执行结果如下:
>>> now()execute now():2015-3-25
3层嵌套的效果是这样的:
>>> now = log('execute')(now)
我们来剖析上面的语句,首先执行log('execute')
,返回的是decorator
函数,再调用返回的函数,参数是now
函数,返回值最终是wrapper
函数。
函数也是对象,它有__name__
等属性,但你去看经过decorator装饰之后的函数,它们的__name__
已经从原来的'now'
变成了'wrapper'
:
>>> now.__name__'wrapper'
Python内置的functools.wraps
解决这个问题的,一个完整的decorator的写法如下:
import functoolsdef log(func): @functools.wraps(func) def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper
或者针对带参数的decorator:
import functoolsdef 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
int()
函数还提供额外的base
参数,默认值为10。如果传入base
参数,就可以做N
进制的转换:
>>> int('12345', base=8)5349>>> int('12345', 16)74565
假设要转换大量的二进制字符串,每次都传入int(x, base=2)
非常麻烦,于是,我们想到,可以定义一个int2()
的函数,默认把base=2
传进去:
def int2(x, base=2): return int(x, base)
functools.partial
就是帮助我们创建一个偏函数的,不需要我们自己定义int2()
,可以直接使用下面的代码创建一个新的函数int2
:
>>> import functools>>> int2 = functools.partial(int, base=2)>>> int2('1000000')64>>> int2('1010101')85
实际上固定了int()
函数的关键字参数base
,也就是:
int2('10010')
相当于:
kw = { 'base': 2 }int('10010', **kw)
转载地址:http://puhpi.baihongyu.com/