Python的map函数理解四式

前言

某人在学习Python函数的时候, 介绍map()函数的使用:

def sqr(x):
    return x ** 2

a = [1, 2, 3]
print map(sqr, a)

这个map函数是python的内嵌的函数, 那么如何手写一个自己的map函数, 实现内嵌map函数一模一样的功能呢?

第一式: 不固定参数

Python内嵌的map函数允许不固定参数, 如下图所示, map第一个参数是一个函数, 剩余的不固定参数只要iterative类型就可以。 譬如,下面可以是元组和数组。

def add(x, y):
    return x + y

a = (2, 3, 4)
b = [10, 5, 3]
print map(add, a, b)

那么如何实现这个不固定参数的读取呢? Python给了*var的形式,可以接受任意参数。 某人根据map的功能进行设计, 先将之后的参数按行拼接成参数, 然后调用函数,得到返回值。

于是,某人实现了如下函数:

def my_map(func, *lst):
    res = []
    m = len(lst)
    n = len(lst[0])
    for i in range(n):
        args = []
        for j in range(m):
            args.append(lst[j][i])
        res.append(func(*args))
    return res

跑一下, 杠杠的!和map()结果一致。

但是, 这个是不是内嵌map的全部实现呢?

第二式: 对齐Iterator,返回默认值

所谓天大差别, 不过一横, 就算差了一点, 任然差别太大。 所以在比较my_map和内嵌的map的时候,是不是还要边界检查。 前面测试了相同长度的数组的map, 对于不同长度的数组的map呢?

a = (2, 3, 4)
b = [10, 5, 3]
print map(lambda x,y: '{}~{}'.format(x,y), a, b+[0])
print my_map(lambda x,y: '{}~{}'.format(x,y),  a, b+[0])

可以上面结构看到, 内嵌的map会按最大长度自动补齐。

那么一种实现,就是找到数据里面最长的, 将所有不够长的补齐None值,之后再调用my_map()

另外一种,我就是利用iterator到结束之后, 默认返回None值。默认的next()函数的第二个参数,可以是返回的默认值。

def my_map2(func, *lst):
    res = []
    its = [iter(it) for it in lst]
    ln = max([len(it)for it in lst])
    while ln > 0:
        args = [next(it,None) for it in its]
        res.append(func(*args))
        ln -= 1
    return res
a = (2, 3, 4)
b = [10, 5, 3]
print map(lambda x,y: '{}~{}'.format(x,y), a, b+[0])
print my_map2(lambda x,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 object>的时候,发现这个map并不可以通过index来访问。

那么基本判断就是一个iterative的对象。 但是如何打印具体内容出来看呢? 最好是通过Python的内部变量之间转换进行list(<map object>)

这样, 那么我们再次调用非对齐的map来测试:

print (list(map(lambda x,y: '{}~{}'.format(x,y), a, b+[0])))

我们发现在Python3里面不再按照最长的iteration来进行对齐了, 而是按照最短的来对齐。 因此, 我们可以把最长换成最短来模拟Python3中的map。

def my_map3(func, *lst):
    res = []
    its = [iter(it) for it in lst]
    ln = min([len(it)for it in lst])
    while ln > 0:
        args = [next(it,None) for it in its]
        res.append(func(*args))
        ln -= 1
    return res

是不是my_map3()在Python3中,就是最好的模拟呢?

第四式: 对齐Iterator,返回默认值

但是毕竟Python3中返回的是一个iterator,一个差异就是, 我们返回一个数组(数组可以通过subscribe[]访问), 我们知道iterator本身,可以通过generator来进行简化, 并且这种lazy实现, 会提高调用效率。 这时候伟大的yield关键词上场了。

一般来说,在Python中, 如何用好yield关键词和decorator功能,都会极大提高工作效率的,也是反应一个人Python功底的东东。

def my_map4(func, *lst):
    its = [iter(it) for it in lst]
    ln = min([len(it)for it in lst])
    while ln > 0:
        args = [next(it,None) for it in its]
        yield func(*args)
        ln -= 1

至此 , 我们可以打印出来,看my_map4返回的也是一个iterative object,这个generator object和map object有着异曲同工之妙。 另外再通过list进行内部变量转化,看到它实现的功能也一致。

至此, 某人听完第三式和第四式的实现, 对Python大大失去兴趣,坑太多! ! !

小结:

本人设计的一个面试题,并且自我回答,通过对Python2和Python3中map函数的测试, 试图自己实现这个内嵌函数, 经验有限,希望大家对map有更为深入的了解,尤其Python2和Python3中实现的巨大差异。 抛砖引玉,也希望看到大家的深刻反馈!以便我进一步更新认知。

原文发布于微信公众号 - AI2ML人工智能to机器学习(mloptimization)

原文发表时间:2017-09-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏数据结构与算法

后缀数组详解

什么是后缀数组 后缀数组是处理字符串的有力工具 —罗穗骞 个人理解:后缀数组是让人蒙逼的有力工具! 就像上面那位大神所说的,后缀数组可以解决很多关于字符串...

37450
来自专栏desperate633

设计模式之工厂方法模式(FACTORY METHOD)问题模拟工厂方法模式分析依赖倒置原则小结

工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。 我们依然接着简单工厂模式提出的披萨店问题继续探讨

10440
来自专栏菜鸟前端工程师

JavaScript学习笔记021-常用排序算法

7220
来自专栏小詹同学

Leetcode打卡 | No.23 合并 k 个有序链表

欢迎和小詹一起定期刷leetcode,每周一和周五更新一题,每一题都吃透,欢迎一题多解,寻找最优解!这个记录帖哪怕只有一个读者,小詹也会坚持刷下去的!

15710
来自专栏程序员互动联盟

【编程基础】C++ Primer快速入门五:实用的模板库

除上篇博客介绍的基本数据类型外,C++ 还定义了一个内容丰富的抽象数据类 型标准库。包括 string 和 vector,它们分别定义了字符串和矢量(集合)。s...

29950
来自专栏vue学习

函数声明提升与变量提升

1.当在函数的作用域里定义一个和外部变量一样的名称的变量时,变量声明会提升至第一句,但是赋值则不变

11120
来自专栏AzMark

Python字符串

18350
来自专栏zhisheng

运算优先级、结合性、求值顺序、副作用和顺序点

标题中这几个概念,是很多C/C++程序员在表达式上容易出问题或不清楚的地方,虽然这些概念在很多语言都有体现,但C里面特别明显,所以就以C语言为例子总结下 运算...

50970
来自专栏从流域到海域

《笨办法学Python》 第29课手记

《笨办法学Python》 第29课手记 本节课讲if语句。 本节内容比较简单,如果觉得你的代码没有错误,但运行时报错,那么你的代码肯定有错误。相信我解释器是已经...

20960
来自专栏Crossin的编程教室

【Python 第18课】 bool类型转换

昨天最后留的几句关于bool类型的转换,其中有一行: bool('False') print一下结果,会发现是True。这是什么原因? 因为在pytho...

25040

扫码关注云+社区

领取腾讯云代金券