【reduce】 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))
【lambda 匿名函数】
用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数:
f = lambda x: x * x f <function at 0x101c6ef28>
f(5) 25 同样,也可以把匿名函数作为返回值返回,比如: def build(x, y): return lambda: x * x + y * y
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))
【 filter()】 def not_empty(s): return s and s.strip()
list(filter(not_empty, ['A', '', 'B', None, 'C', ' ']))
结果: ['A', 'B', 'C']
def is_odd(n): return n % 2 == 1
list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
结果: [1, 5, 9, 15]
【惰性序列】: 不用到它就不计算,要强迫它完成计算 ,需要list() 获取所有结果并返回List
用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, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ... 取新序列的第一个数3,它一定是素数,然后用3把序列的3的倍数筛掉: 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ... 取新序列的第一个数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表示“全体自然数”,“全体素数”这样的序列,而代码非常简洁。
【【闭包】】 注意到返回的函数在其定义内部引用了局部变量args,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用,所以,闭包用起来简单,实现起来可不容易。 另一个需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()才执行。我们来看一个例子: def count(): fs = [] for i in range(1, 4): def f(): return i*i fs.append(f) return fs
f1, f2, f3 = count() 在上面的例子中,每次循环,都创建了一个新的函数,然后,把创建的3个函数都返回了。 你可能认为调用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
由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。
def now(): ... print('2015-3-25') ...
f = now f() 2015-3-25 函数对象有一个__name__属性,可以拿到函数的名字:
now.name 'now'
f.name 'now'
@log def 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()函数。 wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用。在wrapper()函数内,首先打印日志,再紧接着调用原始函数。 如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。比如,要自定义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 和两层嵌套的decorator相比,3层嵌套的效果是这样的:
now = log('execute')(now) 我们来剖析上面的语句,首先执行log('execute'),返回的是decorator函数,再调用返回的函数,参数是now函数,返回值最终是wrapper函数。 以上两种decorator的定义都没有问题,但还差最后一步。因为我们讲了函数也是对象,它有__name__等属性,但你去看经过decorator装饰之后的函数,它们的__name__已经从原来的'now'变成了'wrapper':
now.name 'wrapper' 因为返回的那个wrapper()函数名字就是'wrapper',所以,需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。 不需要编写wrapper.name = func.__name__这样的代码,Python内置的functools.wraps就是干这个事的,所以,一个完整的decorator的写法如下: import functools
def log(func): @functools.wraps(func) def wrapper(*args, **kw): print('call %s():' % func.name) return func(*args, **kw) return wrapper 或者针对带参数的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 import functools是导入functools模块。模块的概念稍候讲解。现在,只需记住在定义wrapper()的前面加上@functools.wraps(func)即可。
但int()函数还提供额外的base参数,默认值为10。如果传入base参数,就可以做N进制的转换:
int('12345', base=8) 5349
int('12345', 16) 74565
functools.partial就是帮助我们创建一个偏函数的,不需要我们自己定义int2(),可以直接使用下面的代码创建一个新的函数int2:
import functools int2 = functools.partial(int, base=2) int2('1000000') 64
int2('1010101') 85
所以,简单总结functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。 注意到上面的新的int2函数,仅仅是把base参数重新设定默认值为2,但也可以在函数调用时传入其他值:
int2('1000000', base=10) 1000000 最后,创建偏函数时,实际上可以接收函数对象、args和**kw这3个参数,当传入: int2 = functools.partial(int, base=2) 实际上固定了int()函数的关键字参数base,也就是: int2('10010') 相当于: kw = { 'base': 2 } int('10010', **kw) 当传入: max2 = functools.partial(max, 10) 实际上会把10作为args的一部分自动加到左边,也就是: max2(5, 6, 7) 相当于: args = (10, 5, 6, 7) max(*args) 结果为10。