社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
私信  •  关注

ninjagecko

ninjagecko 最近创建的主题
ninjagecko 最近回复了
13 年前
回复了 ninjagecko 创建的主题 » 何时在python中使用map()[重复]

以下是一个可能的案例:

map(lambda op1,op2: op1*op2, list1, list2)

对比:

[op1*op2 for op1,op2 in zip(list1,list2)]

我猜如果坚持使用列表理解而不是映射,那么zip()是一个不幸的、不必要的开销。如果有人澄清这一点,无论是肯定的还是否定的。

7 年前
回复了 ninjagecko 创建的主题 » 何时在python中使用map()[重复]

病例

  • 常见病例 :几乎总是,您需要在 蟒蛇 因为你对阅读你的代码的新手做的事情会更明显。(这不适用于其他语言,在这些语言中可能会用到其他习语。)您对python程序员所做的事情将更加明显,因为列表理解是python中用于迭代的事实上的标准;它们是 预期 .
  • 不太常见的情况 :但是,如果您 已经定义了函数 ,通常使用是合理的 map ,尽管它被认为是“不通顺的”。例如, map(sum, myLists) [sum(x) for x in myLists] . 不必构造伪变量(例如 sum(x) for x... sum(_) for _... sum(readableName) for readableName... )你必须输入两次,只是为了迭代。同样的论点适用于 filter reduce 任何来自 itertools 模块:如果你手头已经有一个函数,你可以继续做一些函数编程。这在某些情况下获得可读性,而在其他情况下(例如新手程序员、多个参数)则失去可读性。但是代码的可读性在很大程度上取决于您的注释。
  • 几乎从不 :您可能要使用 地图 函数作为一个纯抽象函数进行函数编程 地图 ,或咖喱 地图 ,或者从谈论 地图 作为一种功能。例如,在haskell中,一个名为 fmap 泛化任何数据结构上的映射。这在python中是非常少见的,因为python语法迫使您使用生成器样式来讨论迭代;您不能很容易地将其泛化。(这有时是好的,有时是坏的)您可能会想出一些罕见的python示例,其中 map(f, *lists) 是一件合理的事情。我能想到的最接近的例子是 sumEach = partial(map,sum) ,这是一行,大致相当于:

def sumEach(myLists):
    return [sum(_) for _ in myLists]
  • 只使用一个 for 回路 :当然也可以使用for循环。虽然从函数式编程的角度来看,非局部变量并没有那么优雅,但有时在命令式编程语言(如python)中,非局部变量会使代码更加清晰,因为人们非常习惯这样阅读代码。因为循环通常也是最有效的,当你只是做任何不建立类似列表的复杂操作时,列表理解和映射被优化(例如,求和或生成树等),至少在内存方面是有效的(不一定在时间上是有效的,最坏的情况下,除了一些罕见的病态垃圾收集打嗝之外,还有一个不变的因素)。

“巨蛇座”

我不喜欢“pythonic”这个词,因为我觉得pythonic在我眼里并不总是优雅的。尽管如此, 地图 滤波器 以及类似的函数(比如 迭代工具 模块)在风格上可能被认为是不通俗的。

懒惰

就效率而言,就像大多数函数式编程结构一样, 地图可能很懒 ,实际上在python中是懒惰的。这意味着你可以这样做(在 Python 3 )并且您的计算机不会耗尽内存并丢失所有未保存的数据:

>>> map(str, range(10**100))
<map object at 0x2201d50>

试着用一个清单来理解:

>>> [str(n) for n in range(10**100)]
# DO NOT TRY THIS AT HOME OR YOU WILL BE SAD #

请注意,列表理解本身也是懒惰的,但是 python选择将它们实现为非懒惰的 . 不过,python确实支持以生成器表达式的形式进行惰性列表理解,如下所示:

>>> (str(n) for n in range(10**100))
<generator object <genexpr> at 0xacbdef>

你基本上可以想到 [...] 将生成器表达式传递给列表构造函数的语法,如 list(x for x in range(5)) .

捏造的简单例子

from operator import neg
print({x:x**2 for x in map(neg,range(5))})

print({x:x**2 for x in [-y for y in range(5)]})

print({x:x**2 for x in (-y for y in range(5))})

列表理解是非懒惰的,因此可能需要更多的内存(除非您使用生成器理解)。方括号 […] 经常使事情变得明显,尤其是在一堆括号里。另一方面,有时你会像打字一样冗长 [x for x in... . 只要您保持迭代器变量简短,如果您不缩进代码,列表理解通常会更清晰。但是你可以一直缩进你的代码。

print(
    {x:x**2 for x in (-y for y in range(5))}
)

或者分手:

rangeNeg5 = (-y for y in range(5))
print(
    {x:x**2 for x in rangeNeg5}
)

蟒蛇3号的效率比较

地图 现在懒惰:

% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=map(f,xs)'
1000000 loops, best of 3: 0.336 usec per loop            ^^^^^^^^^

因此,如果您不使用所有数据,或者不提前知道您需要多少数据, 地图 在python3中(以及python2或python3中的生成器表达式),在最后一刻之前,将避免计算它们的值。通常这将超过使用 地图 . 缺点是,与大多数函数式语言相比,这在python中是非常有限的:只有在从左到右“顺序”访问数据时,才能获得这一好处,因为python生成器表达式只能按顺序计算 x[0], x[1], x[2], ... .

不过,假设我们有一个预先生成的函数 f 我们愿意 地图 ,我们忽略了 地图 立即强制评估 list(...) . 我们得到了一些非常有趣的结果:

% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=list(map(f,xs))'                                                                                                                                                
10000 loops, best of 3: 165/124/135 usec per loop        ^^^^^^^^^^^^^^^
                    for list(<map object>)

% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=[f(x) for x in xs]'                                                                                                                                      
10000 loops, best of 3: 181/118/123 usec per loop        ^^^^^^^^^^^^^^^^^^
                    for list(<generator>), probably optimized

% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=list(f(x) for x in xs)'                                                                                                                                    
1000 loops, best of 3: 215/150/150 usec per loop         ^^^^^^^^^^^^^^^^^^^^^^
                    for list(<generator>)

结果以a a a/bbb/ccc的形式显示,其中a是在使用python 3的circa-2010英特尔工作站上执行的。??,b和c是在一个使用python 3.2.1的circa-2013 amd工作站上执行的,硬件非常不同。结果表明,映射和列表理解在性能上具有可比性,且受其他随机因素的影响最大。奇怪的是,我们唯一能说的似乎是,当我们期望列表理解时 […] 执行比生成器表达式更好的表达式 (...) , 地图 生成器表达式的效率也更高(再次假设计算/使用了所有值)。

重要的是要认识到这些测试假设了一个非常简单的函数(identity函数);但是这很好,因为如果函数很复杂,那么与程序中的其他因素相比,性能开销可以忽略不计。(用其他一些简单的东西来测试,比如 f=lambda x:x+x )

如果您擅长阅读python程序集,则可以使用 dis 模块查看这是否是幕后发生的事情:

>>> listComp = compile('[f(x) for x in xs]', 'listComp', 'eval')
>>> dis.dis(listComp)
  1           0 LOAD_CONST               0 (<code object <listcomp> at 0x2511a48, file "listComp", line 1>) 
              3 MAKE_FUNCTION            0 
              6 LOAD_NAME                0 (xs) 
              9 GET_ITER             
             10 CALL_FUNCTION            1 
             13 RETURN_VALUE         
>>> listComp.co_consts
(<code object <listcomp> at 0x2511a48, file "listComp", line 1>,)
>>> dis.dis(listComp.co_consts[0])
  1           0 BUILD_LIST               0 
              3 LOAD_FAST                0 (.0) 
        >>    6 FOR_ITER                18 (to 27) 
              9 STORE_FAST               1 (x) 
             12 LOAD_GLOBAL              0 (f) 
             15 LOAD_FAST                1 (x) 
             18 CALL_FUNCTION            1 
             21 LIST_APPEND              2 
             24 JUMP_ABSOLUTE            6 
        >>   27 RETURN_VALUE

>>> listComp2 = compile('list(f(x) for x in xs)', 'listComp2', 'eval')
>>> dis.dis(listComp2)
  1           0 LOAD_NAME                0 (list) 
              3 LOAD_CONST               0 (<code object <genexpr> at 0x255bc68, file "listComp2", line 1>) 
              6 MAKE_FUNCTION            0 
              9 LOAD_NAME                1 (xs) 
             12 GET_ITER             
             13 CALL_FUNCTION            1 
             16 CALL_FUNCTION            1 
             19 RETURN_VALUE         
>>> listComp2.co_consts
(<code object <genexpr> at 0x255bc68, file "listComp2", line 1>,)
>>> dis.dis(listComp2.co_consts[0])
  1           0 LOAD_FAST                0 (.0) 
        >>    3 FOR_ITER                17 (to 23) 
              6 STORE_FAST               1 (x) 
              9 LOAD_GLOBAL              0 (f) 
             12 LOAD_FAST                1 (x) 
             15 CALL_FUNCTION            1 
             18 YIELD_VALUE          
             19 POP_TOP              
             20 JUMP_ABSOLUTE            3 
        >>   23 LOAD_CONST               0 (None) 
             26 RETURN_VALUE

>>> evalledMap = compile('list(map(f,xs))', 'evalledMap', 'eval')
>>> dis.dis(evalledMap)
  1           0 LOAD_NAME                0 (list) 
              3 LOAD_NAME                1 (map) 
              6 LOAD_NAME                2 (f) 
              9 LOAD_NAME                3 (xs) 
             12 CALL_FUNCTION            2 
             15 CALL_FUNCTION            1 
             18 RETURN_VALUE 

看来还是用 […] 语法比 名单(…) . 可悲的是 地图 类对于反汇编来说有点不透明,但是我们可以通过速度测试来实现。