使用 map() 与 for 是否有价值?
map() 是否像“for”一样迭代列表? 使用 map 与 for 是否有价值?
如果是这样,现在我的代码如下所示:
for item in items:
item.my_func()
如果有意义,我想将其设为map()。 那可能吗? 一个例子是什么样的?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
您可以使用
map
而不是您已经展示了for
循环,但由于您似乎没有使用item.my_func()
的结果,因此不推荐。 如果您想对列表的所有元素应用没有副作用的函数,则应使用map
。 在所有其他情况下,请使用显式 for 循环。另外,从 Python 3.0
map
返回一个迭代器,因此在这种情况下,map
的行为不会相同(除非您显式评估迭代器返回的所有元素,例如通过调用列表
)。编辑:kibibu在评论中要求澄清为什么
map
的第一个参数不应该是有副作用的函数。 我将尝试回答这个问题:map
应该传递一个函数f
数学意义上的。 在这种情况下,将f
应用于第二个参数的元素的顺序并不重要(当然,只要它们按原始顺序返回即可)。 更重要的是,在这种情况下,map(g, map(f, l))
在语义上等同于map(lambda x: g(f(x)), l)
,无论f
和g
应用于各自输入的顺序如何。例如,
map
是否立即返回迭代器或完整列表并不重要。 但是,如果f
和/或g
引起副作用,则仅当map(g, map(f, l)) 的语义符合时才能保证这种等价性
使得在任何阶段g
都应用于map(f, l)
在n 个元素>map(f, l)
将f
应用到l
的 *(n + 1)* st 元素。 (这意味着map
必须执行最惰性的迭代——Python 3 中是这样做的,但 Python 2 中不是!)更进一步:即使我们假设
的 Python 3 实现>map
,如果map(f, l)
的输出例如通过itertools.tee
在提供给外部map
调用之前。上述讨论似乎是理论上的,但随着程序变得越来越复杂,它们变得更难以推理,因此更难调试。 确保某些事情不变可以在一定程度上缓解该问题,并且实际上可以防止一整类错误。
最后,
map
提醒许多人它在各种(纯)函数式语言中的真正函数式对应物。 向它传递一个带有副作用的“函数”会让那些人感到困惑。 因此,鉴于替代方案(即使用显式循环)并不比调用map
更难实现,因此强烈建议将map
的使用限制为要应用的功能不会引起副作用的情况。You could use
map
instead of thefor
loop you've shown, but since you do not appear to use the result ofitem.my_func()
, this is not recommended.map
should be used if you want to apply a function without side-effects to all elements of a list. In all other situations, use an explicit for-loop.Also, as of Python 3.0
map
returns an iterator, so in that casemap
will not behave the same (unless you explicitly evaluate all elements returned by the iterator, e.g. by callinglist
on it).Edit: kibibu asks in the comments for a clarification on why
map
's first argument should not be a function with side effects. I'll give answering that question a shot:map
is meant to be passed a functionf
in the mathematical sense. Under such circumstances it does not matter in which orderf
is applied to the elements of the second argument (as long as they are returned in their original order, of course). More importantly, under those circumstancesmap(g, map(f, l))
is semantically equivalent tomap(lambda x: g(f(x)), l)
, regardless of the order in whichf
andg
are applied to their respective inputs.E.g., it doesn't matter whether
map
returns an iterator or a full list at once. However, iff
and/org
cause side effects, then this equivalence is only guaranteed if the semantics ofmap(g, map(f, l))
are such that at any stageg
is applied to the first n elements returned bymap(f, l)
beforemap(f, l)
appliesf
to the *(n + 1)*st element ofl
. (Meaning thatmap
must perform the laziest possible iteration---which it does in Python 3, but not in Python 2!)Going one step further: even if we assume the Python 3 implementation of
map
, the semantic equivalence may easily break down if the output ofmap(f, l)
is e.g. passed throughitertools.tee
before being supplied to the outermap
call.The above discussion may seem of a theoretic nature, but as programs become more complex, they become more difficult to reason about and therefore harder to debug. Ensuring that some things are invariant alleviates that problem somewhat, and may in fact prevent a whole class of bugs.
Lastly,
map
reminds many people of its truly functional counterpart in various (purely) functional languages. Passing it a "function" with side effects will confuse those people. Therefore, seeing as the alternative (i.e., using an explicit loop) is not harder to implement than a call tomap
, it is highly recommended that one restricts use ofmap
to those cases in which the function to be applied does not cause side effects.您可以使用 map 来编写此代码,如下所示:
将 cls 替换为您要迭代的项目的类。
正如 Stephan202 所提到的,在这种情况下不建议这样做。
通常,如果您想通过对列表中的每个项目应用某些函数来创建新列表,请使用 map. 这意味着该函数没有副作用,因此您可以(可能)并行运行该映射。
如果您不想创建新列表,或者该函数有副作用,请使用 for 循环。 您的示例就是这种情况。
You can write this using map like this:
replacing cls with the class of the items you are iterating over.
As mentioned by Stephan202, this is not recommended in this case.
As a rule, if you want to create a new list by applying some function to each item in the list, use map. This has the implied meaning that the function has no side effect, and thus you could (potentially) run the map in parallel.
If you don't want to create a new list, or if the function has side effects, use a for loop. This is the case in your example.
存在轻微的语义差异,这可能在 python 语言规范中已关闭。 map 是显式可并行的,而 for 仅在特殊情况下。 代码可以从 for 中中断,但只能从 map 中转义(例外)。
在我看来,map 不应该保证函数应用的顺序,而for 则必须保证。 AFAIK 目前没有 python 实现能够执行此自动并行化。
There is a slight semantic difference, which is probably closed in python language spec. The map is explicitly parallelizable, while for only in special situations. Code can break out from for, but only escape with exception from map.
In my opinion map shouldn't also guarantee order of function application while for must. AFAIK no python implementation is currently able to do this auto-parallelization.
如果需要,您可以将您的
map
切换到一些很酷的线程或多处理或分布式计算框架。 Disco 是一个基于 erlang-and-python 框架的分布式、抗故障示例。 我将其配置在 2 个 8 核的盒子上,现在我的程序运行速度提高了 16 倍,这要归功于 Disco 集群,但是我必须从列表推导和 for 循环重写我的程序以进行映射/归约。使用 for 循环、列表推导式和 map/reduce 编写程序是同样的事情,但是当您需要它在集群上运行时,如果您使用 map/reduce,您几乎可以免费完成它。 如果你没有,那么,你将不得不重写。
请注意:据我所知,python 2.x 从映射返回一个列表而不是迭代器。 我听说可以通过使用 iter.imap() 来绕过这个问题(但从未使用过)。
You can switch Your
map
to some cool threaded OR multiprocessing OR distributed computing framework if You need to. Disco is an example of distributed, resistant to failures erlang-and-python based framework. I configured it on 2 boxes of 8 cores and now my program runs 16 times faster, thanks to the Disco cluster, however I had to rewrite my program from list comprehensions and for loops to map/reduce.It's the same deal to write a program using for loops and list comprehensions and map/reduce, but when You need it to run on a cluster, You can do it almost for free if You used map/reduce. If You didn't, well, You will have to rewrite.
Beware: as far as I know, python 2.x returns a list instead of an iterator from map. I've heard this can be bypassed by using
iter.imap()
(never used it though).当您不需要返回结果列表(例如具有副作用的函数)时,请使用显式 for 循环。
当您确实需要返回结果列表时,请使用列表理解(例如,直接根据输入返回值的函数)。
当你试图让 Lisp 用户相信 Python 值得使用时,请使用 map()。 ;)
Use an explicit for-loop when you don't need a list of results back (eg. functions with side-effects).
Use a list comprehension when you do need a list of results back (eg. functions that return a value based directly on the input).
Use map() when you're trying to convince Lisp users that Python is worth using. ;)
map
的主要优点是当您想要获取列表中每个元素的某些计算结果时。 例如,此代码片段将列表中的每个值加倍:值得注意的是,
map
返回一个包含结果的新列表。 它不会就地修改原始列表。要使用
for
执行相同的操作,您必须创建一个空列表,并向for
主体添加额外的行,以将每个计算的结果添加到新列表中。map
版本更加简洁和实用。The main advantage of
map
is when you want to get the result of some calculation on every element in a list. For example, this snippet doubles every value in a list:It is important to note that
map
returns a new list with the results. It does not modify the original list in place.To do the same thing with
for
, you would have to create an empty list and add an extra line to thefor
body to add the result of each calculation to the new list. Themap
version is more concise and functional.对于内置函数来说,Map 有时比手动编写 for 循环更快。 尝试使用 Timing map(str, range(1000000)) 与类似的 for 循环。
Map can sometimes be faster for built-in functions than manually coding a for loop. Try timing map(str, range(1000000)) vs. a similar for loop.
已经有很多好的答案了。 我想强调
map()
的另一个方面:代码可读性和透明度。特别是,我将重点关注问题的这一部分:
对我来说,在适当的情况下使用
map()
而不是循环或推导式,其主要好处是使代码更加透明。(为简单起见,下面我假设我们正在
list
上进行操作,并且我假装map()
返回一个list [1])
对于阅读或浏览代码的人来说,使用
map()
转换的列表可以为读者提供额外的信息,而且成本非常低。 这些信息分为两类:使用
map()
,而不是循环或理解,请注意,在 Python 社区中,通常认为使用推导式或 for 循环比
map()
更符合 Python 风格。[1]:
map()
的返回类型和惰性求值map()
接受一个可迭代,并且返回一些可迭代的东西 -就像生成器的形式一样。可迭代类型的示例是列表和元组。
您可以将返回值视为迭代器,但仅在需要时才会计算该值。 后者是
map()
实际上返回一个生成器的结果。 这称为惰性求值。我们可以用函数
prnt()
来说明该行为,该函数返回None
并具有打印其副作用控制台的参数:现在,
There's a number of good answers already. I want to emphasis another aspect of
map()
: code readability and transparency.Particularly, I'll focus on this part of the question:
For me, using
map()
instead of loops or comprehensions, where appropriate, has the main benefit of making the code more transparet.(For simplicity, below I assume that we're operating on a
list
, and I pretend thatmap()
returns alist
. [1])For someone reading or glancing over the code, a list transformed with
map()
gives the reader additional information, and does so very cheaply. This information comes in two categories:With
map()
, rather than a loop or comprehension,Note that within the Python community, it is generally considered more pythonic to use comprehensions or for loops rather than
map()
.[1]: Return type of
map()
and lazy evaluationmap()
takes an iterable, and it returns something iterable-like in the form of a generator.An example of iterable types are list and tuples.
You can treat the returned value as an iterator, but the value will be evaluated only when needed. The latter is a consequence of
map()
actually returning a generator. This is called lazy evaluation.We can illustrate the behaviour with a function
prnt()
that returnsNone
and has the side effect of printing its argument to the console:Now,