Python 列表理解vs地图

Python 列表理解vs地图,python,list-comprehension,map-function,Python,List Comprehension,Map Function,是否有理由更喜欢使用map()而不是列表理解?它们中的一个通常比另一个更有效,还是被认为比另一个更具pythonic?map在某些情况下可能会在显微镜下更快(当您不是为此目的制作lambda,而是在map和listcomp中使用相同的函数时)。列表理解可能在其他情况下更快,大多数(并非全部)PythOnistas认为它们更直接和更清楚。 使用完全相同的功能时map的微小速度优势示例: $ python -mtimeit -s'xs=range(10)' 'map(hex, xs)' 100000

是否有理由更喜欢使用
map()
而不是列表理解?它们中的一个通常比另一个更有效,还是被认为比另一个更具pythonic?

map
在某些情况下可能会在显微镜下更快(当您不是为此目的制作lambda,而是在map和listcomp中使用相同的函数时)。列表理解可能在其他情况下更快,大多数(并非全部)PythOnistas认为它们更直接和更清楚。 使用完全相同的功能时map的微小速度优势示例:

$ python -mtimeit -s'xs=range(10)' 'map(hex, xs)'
100000 loops, best of 3: 4.86 usec per loop
$ python -mtimeit -s'xs=range(10)' '[hex(x) for x in xs]'
100000 loops, best of 3: 5.58 usec per loop
%timeit map(sum, x_list)  # creating iterator object
# Output: The slowest run took 9.91 times longer than the fastest. 
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 277 ns per loop

%timeit list(map(sum, x_list))  # creating list with map
# Output: 1000 loops, best of 3: 214 µs per loop

%timeit [sum(x) for x in x_list]  # creating list with list comprehension
# Output: 1000 loops, best of 3: 290 µs per loop
%timeit map(lambda i: i+1, i_list)
# Output: The slowest run took 8.64 times longer than the fastest. 
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 325 ns per loop

%timeit list(map(lambda i: i+1, i_list))
# Output: 1000 loops, best of 3: 183 µs per loop

%timeit [i+1 for i in i_list]
# Output: 10000 loops, best of 3: 84.2 µs per loop
当map需要lambda时,性能比较如何完全颠倒的示例:

$ python -mtimeit -s'xs=range(10)' 'map(lambda x: x+2, xs)'
100000 loops, best of 3: 4.24 usec per loop
$ python -mtimeit -s'xs=range(10)' '[x+2 for x in xs]'
100000 loops, best of 3: 2.32 usec per loop

我发现列表理解通常比映射更能表达我要做的事情-它们都能完成,但前者省去了试图理解复杂的lambda表达式的精神负担


还有一次采访(我一时找不到),Guido将lambda和函数函数列为他最后悔接受Python的内容,因此你可以提出这样的论点,即它们是非Python的。

这里有一个可能的例子:

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

[op1*op2 for op1,op2 in zip(list1,list2)]
我猜如果坚持使用列表理解而不是地图,那么zip()是一种不幸的、不必要的开销。如果有人明确或否定地澄清这一点,那就太好了。

案例

  • 常见情况:几乎总是希望在python中使用列表理解,因为对于阅读代码的新手程序员来说,这样做更为明显。(这不适用于其他语言,因为其他习惯用法可能适用。)对于python程序员来说,这将更加明显,因为列表理解是python中迭代的事实标准;他们是被期待的
  • 不太常见的情况:但是,如果您已经定义了一个函数,那么使用
    map
    通常是合理的,尽管它被认为是“非音速的”。例如,
    map(sum,mylist)
    [mylist中x的sum(x)]
    更优雅/简洁。您获得了不必组成虚拟变量的优雅性(例如,
    sum(x)代表x…
    sum(uuu)代表
    sum(readableName)代表readableName…
    ),您只需键入两次即可进行迭代。对于
    filter
    reduce
    以及
    itertools
    模块中的任何内容,同样的论点也适用:如果您手头已经有一个函数,您可以继续进行一些函数编程。在某些情况下,这会增加可读性,而在其他情况下(例如,新手程序员、多个参数)则会失去可读性。。。但是代码的可读性在很大程度上取决于您的注释
  • 几乎从不使用
    map:在进行函数式编程时,您可能希望将
    map
    函数作为纯抽象函数来使用,其中您正在映射
    map
    ,或者制作
    map
    ,或者将
    map
    作为函数来讨论。例如,在Haskell中,一个名为
    fmap
    的函子接口概括了任何数据结构上的映射。这在python中非常少见,因为python语法迫使您使用生成器样式来讨论迭代;你不能轻易概括它。(这有时是好的,有时是坏的。)您可能会想出一些罕见的python示例,其中
    map(f,*list)
    是一件合理的事情。我能想到的最接近的例子是
    sumEach=partial(map,sum)
    ,这是一个简单的线性关系,大致相当于:

  • 只使用
    for
    -循环
    :当然也可以只使用for循环。从函数式编程的角度来看,虽然不是很优雅,但在命令式编程语言(如python)中,有时非局部变量会使代码更清晰,因为人们非常习惯以这种方式阅读代码。For循环通常也是最有效的,当您仅在执行任何不构建列表的复杂操作时(例如,求和或生成树等),列表理解和映射会得到优化,至少在内存方面是有效的(不一定是在时间方面,在最坏的情况下,我希望是一个常数,除非是一些罕见的病态垃圾收集打嗝)
“Pythonism”

我不喜欢“pythonic”这个词,因为我觉得pythonic在我眼中并不总是优雅的。然而,
map
filter
以及类似的函数(比如非常有用的
itertools
模块)在风格上可能被认为是不和谐的

懒惰

就效率而言,与大多数函数式编程结构一样,映射可以是惰性的,实际上在python中也是惰性的。这意味着您可以做到这一点(在python3中),并且您的计算机不会耗尽内存并丢失所有未保存的数据:

>>> map(str, range(10**100))
<map object at 0x2201d50>
请注意,列表理解本身也是惰性的,但python选择将其实现为非惰性的。尽管如此,python确实以生成器表达式的形式支持惰性列表理解,如下所示:

>>> (str(n) for n in range(10**100))
<generator object <genexpr> at 0xacbdef>
列表理解是非惰性的,因此可能需要更多内存(除非您使用生成器理解)。方括号
[…]
通常会让事情变得明显,尤其是在括号混乱的情况下。另一方面,有时您会像键入
[x代表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}
)
python3的效率比较

map
现在是惰性的:

% 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 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=map(f,xs)'
1000000 loops, best of 3: 0.336 usec per loop            ^^^^^^^^^
% 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>)
>>> 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 
for x, y in somePoints:
    # (several lines of code here)
    squared = [x ** 2 for x in numbers]
    # Oops, x was silently overwritten!
for x, y in somePoints:
    # (several lines of code here)
    squared = map(lambda x: x ** 2, numbers)
def square(x):
    return x*x
squares = map(square, [1, 2, 3])
print(list(squares))
print(list(squares))
x_list = [(i, i+1, i+2, i*2, i-9) for i in range(1000)]
i_list = list(range(1000))
%timeit map(sum, x_list)  # creating iterator object
# Output: The slowest run took 9.91 times longer than the fastest. 
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 277 ns per loop

%timeit list(map(sum, x_list))  # creating list with map
# Output: 1000 loops, best of 3: 214 µs per loop

%timeit [sum(x) for x in x_list]  # creating list with list comprehension
# Output: 1000 loops, best of 3: 290 µs per loop
%timeit map(lambda i: i+1, i_list)
# Output: The slowest run took 8.64 times longer than the fastest. 
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 325 ns per loop

%timeit list(map(lambda i: i+1, i_list))
# Output: 1000 loops, best of 3: 183 µs per loop

%timeit [i+1 for i in i_list]
# Output: 10000 loops, best of 3: 84.2 µs per loop
%timeit (sum(i) for i in x_list)
# Output: The slowest run took 6.66 times longer than the fastest. 
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 495 ns per loop

%timeit list((sum(x) for x in x_list))
# Output: 1000 loops, best of 3: 319 µs per loop

%timeit (i+1 for i in i_list)
# Output: The slowest run took 6.83 times longer than the fastest. 
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 506 ns per loop

%timeit list((i+1 for i in i_list))
# Output: 10000 loops, best of 3: 125 µs per loop
In [1]: odd_cubes = [x ** 3 for x in range(10) if x % 2 == 1] # using a list comprehension

In [2]: odd_cubes_alt = list(map(lambda x: x ** 3, filter(lambda x: x % 2 == 1, range(10)))) # using map and filter

In [3]: odd_cubes == odd_cubes_alt
Out[3]: True
class DummyNum(object):
    """Dummy class"""
    __slots__ = 'n',

    def __init__(self, n):
        self.n = n

    def add(self):
        self.n += 5
python -mtimeit -s "xs=range(123456)" "map(hex, xs)"
1000000 loops, best of 5: 218 nsec per loop
python -mtimeit -s "xs=range(123456)" "[hex(x) for x in xs]"
10 loops, best of 5: 19.4 msec per loop