假设有一个函数,这个函数需要接收4个参数,并返回这4个参数的和:
def sum_four(a, b, c, d):
return a + b + c + d
如果需要固定最后前三个参数,仅改变最后一个参数的值,这时候可能需要这么调用:
> > > a, b, c = 1, 2, 3
> > > sum_four(a=a, b=b, c=c, d=1)
7
> > > sum_four(a=a, b=b, c=c, d=2)
8
> > > sum_four(a=a, b=b, c=c, d=3)
9
> > > sum_four(a=a, b=b, c=c, d=4)
10
这样写实在是太丑了,如果用 Map 函数,是否能简化代码?
答案是肯定的,但是Map函数【一般】只能接受单一元素,如果你强行使用的话,它会报这样的错:
> > > list(map(sum_four, [(1, 2, 3, 4)]))
Traceback (most recent call last):
File "< stdin >", line 1, in < module >
TypeError: sum_four() missing 3 required positional arguments: 'b', 'c', and 'd'
怎么解决?
可以使用 itertools 的函数 starmap 替换Map.
它与Map不同,允许接受一个元组作为传入sum_four的参数。
> > > import itertools
> > > list(itertools.starmap(sum_four, [(1, 2, 3, 4)]))
[10]
非常棒,这样的话,上述问题就可以使用starmap函数解决:
> > > import itertools
> > > ds = [1, 2, 3, 4]
> > > items = ((a, b, c, d) for d in ds)
> > > list(items)
[(1, 2, 3, 1), (1, 2, 3, 2), (1, 2, 3, 3), (1, 2, 3, 4)]
> > > list(itertools.starmap(sum_four, items))
[7, 8, 9, 10]
请注意 items 是一个生成器,这是为了避免 items 过大导致内存消耗量过大。平时开发的时候注意这些细节,能够使你和普通的开发者拉开差距。
第二种解决方案是使用 partial 函数固定前三个参数。
根据文档,partial
将“冻结”函数的参数的某些部分,从而生成简化版的函数。
因此上述问题的解决方案就是:
> > > import functools
> > > partial_sum_four = functools.partial(sum_four, a, b, c)
> > > partial_sum_four(3)
9
> > > # 这样就可以使用map函数了:
> > > list(map(partial_sum_four, ds))
[7, 8, 9, 10]
事实上,Map 函数是允许传递可迭代参数的,但是有一个有趣的特点,他会用每个可迭代对象里的项作为传入函数的不同参数。这样说可能太过于抽象了,来看看实际的例子:
> > > list(map(sum_four, [1,1,1,1], [2,2,2,2], [3,3,3,3], [1,2,3,4]))
[7, 8, 9, 10]
明显,每次都使用了不同数组中对应下标的项传入函数进行计算。
这样,我们可以使用这个特点进行优化。
itertools.repeat() 函数能够根据参数产生一个迭代器,该迭代器一次又一次返回对象。不指定times参数,它将无限期运行。
而 Map 函数会在最短的可迭代对象被迭代完后,就会自动停止运行。
结合这两个特点,上述问题的解决方案就出来了:
> > > import itertools
> > > list(map(sum_four, itertools.repeat(a), itertools.repeat(b), itertools.repeat(c), ds))
[7, 8, 9, 10]
这招非常巧妙,缺点是能读懂的人不多。不过没关系,计算机世界中某些东西知道就好,你并不一定需要去使用它。
全部0条评论
快来发表一下你的评论吧 !