前言
某人在学习Python函数的时候, 介绍map()函数的使用:
defsqr(x):
returnx **2
a = [1,2,3]
printmap(sqr, a)
这个map函数是python的内嵌的函数, 那么如何手写一个自己的map函数,实现内嵌map函数一模一样的功能呢?
第一式: 不固定参数
Python内嵌的map函数允许不固定参数, 如下图所示, map第一个参数是一个函数, 剩余的不固定参数只要iterative类型就可以。 譬如,下面可以是元组和数组。
defadd(x, y):
returnx + y
a = (2,3,4)
b = [10,5,3]
printmap(add, a, b)
那么如何实现这个不固定参数的读取呢?Python给了*var的形式,可以接受任意参数。 某人根据map的功能进行设计, 先将之后的参数按行拼接成参数, 然后调用函数,得到返回值。
于是,某人实现了如下函数:
defmy_map(func, *lst):
res = []
m =len(lst)
n =len(lst[0])
foriinrange(n):
args = []
forjinrange(m):
args.append(lst[j][i])
res.append(func(*args))
returnres
跑一下, 杠杠的!和map()结果一致。
但是, 这个是不是内嵌map的全部实现呢?
第二式: 对齐Iterator,返回默认值
所谓天大差别, 不过一横, 就算差了一点, 任然差别太大。 所以在比较my_map和内嵌的map的时候,是不是还要边界检查。 前面测试了相同长度的数组的map, 对于不同长度的数组的map呢?
a = (2,3,4)
b = [10,5,3]
printmap(lambdax,y:'{}~{}'.format(x,y), a,b+[0])
printmy_map(lambdax,y:'{}~{}'.format(x,y), a,b+[0])
可以上面结构看到, 内嵌的map会按最大长度自动补齐。
那么一种实现,就是找到数据里面最长的, 将所有不够长的补齐None值,之后再调用my_map()
另外一种,我就是利用iterator到结束之后, 默认返回None值。默认的next()函数的第二个参数,可以是返回的默认值。
defmy_map2(func, *lst):
res = []
its = [iter(it)foritinlst]
ln =max([len(it)foritinlst])
whileln >0:
args = [next(it,None)foritinits]
res.append(func(*args))
ln -=1
returnres
a = (2,3,4)
b = [10,5,3]
printmap(lambdax,y:'{}~{}'.format(x,y), a, b+[0])
printmy_map2(lambdax,y:'{}~{}'.format(x,y), a, b+[0])
效果不错,那么是不是就搞定了呢?
第三式: Python 2,3兼容,内部类型变换
是不是上面实现的my_map2(),就是一个python2和python3下都和map函数一致呢? 前面,我们在使用print函数的时候, 没有使用括号, 那么明显是在Python2的环境下面, 那么对于Python3环境下呢?在Python3下测试一下吧:
我们发现打印了是一个map object。嘿, 内嵌map函数的实现, 在Python2和Python3中似乎很不一样。 如果我们试图通过subscribe[]来访问的的时候,发现这个map并不可以通过index来访问。
那么基本判断就是一个iterative的对象。 但是如何打印具体内容出来看呢? 最好是通过Python的内部变量之间转换进行list()
这样, 那么我们再次调用非对齐的map来测试:
print(list(map(lambdax,y:'{}~{}'.format(x,y), a, b+[0])))
我们发现在Python3里面不再按照最长的iteration来进行对齐了, 而是按照最短的来对齐。因此, 我们可以把最长换成最短来模拟Python3中的map。
defmy_map3(func, *lst):
res = []
its = [iter(it)foritinlst]
ln =min([len(it)foritinlst])
whileln >0:
args = [next(it,None)foritinits]
res.append(func(*args))
ln -=1
returnres
是不是my_map3()在Python3中,就是最好的模拟呢?
第四式: 对齐Iterator,返回默认值
但是毕竟Python3中返回的是一个iterator,一个差异就是, 我们返回一个数组(数组可以通过subscribe[]访问), 我们知道iterator本身,可以通过generator来进行简化,并且这种lazy实现, 会提高调用效率。 这时候伟大的yield关键词上场了。
一般来说,在Python中, 如何用好yield关键词和decorator功能,都会极大提高工作效率的,也是反应一个人Python功底的东东。
defmy_map4(func, *lst):
its = [iter(it)foritinlst]
ln =min([len(it)foritinlst])
whileln >0:
args = [next(it,None)foritinits]
yieldfunc(*args)
ln -=1
至此 ,我们可以打印出来,看my_map4返回的也是一个iterative object,这个generator object和map object有着异曲同工之妙。 另外再通过list进行内部变量转化,看到它实现的功能也一致。
至此, 某人听完第三式和第四式的实现, 对Python大大失去兴趣,坑太多! ! !
小结:
本人设计的一个面试题,并且自我回答,通过对Python2和Python3中map函数的测试, 试图自己实现这个内嵌函数, 经验有限,希望大家对map有更为深入的了解,尤其Python2和Python3中实现的巨大差异。 抛砖引玉,也希望看到大家的深刻反馈!以便我进一步更新认知。