使用 map() 与 for 是否有价值?

发布于 2024-07-22 05:35:30 字数 190 浏览 11 评论 0 原文

map() 是否像“for”一样迭代列表? 使用 map 与 for 是否有价值?

如果是这样,现在我的代码如下所示:

for item in items:
    item.my_func()

如果有意义,我想将其设为map()。 那可能吗? 一个例子是什么样的?

Does map() iterate through the list like "for" would? Is there a value in using map vs for?

If so, right now my code looks like this:

for item in items:
    item.my_func()

If it makes sense, I would like to make it map(). Is that possible? What is an example like?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

⒈起吃苦の倖褔 2024-07-29 05:35:30

您可以使用 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)无论 fg 应用于各自输入的顺序如何

例如,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 the for loop you've shown, but since you do not appear to use the result of item.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 case map will not behave the same (unless you explicitly evaluate all elements returned by the iterator, e.g. by calling list 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 function f in the mathematical sense. Under such circumstances it does not matter in which order f 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 circumstances map(g, map(f, l)) is semantically equivalent to map(lambda x: g(f(x)), l), regardless of the order in which f and g are applied to their respective inputs.

E.g., it doesn't matter whether map returns an iterator or a full list at once. However, if f and/or g cause side effects, then this equivalence is only guaranteed if the semantics of map(g, map(f, l)) are such that at any stage g is applied to the first n elements returned by map(f, l) before map(f, l) applies f to the *(n + 1)*​st element of l. (Meaning that map 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 of map(f, l) is e.g. passed through itertools.tee before being supplied to the outer map 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 to map, it is highly recommended that one restricts use of map to those cases in which the function to be applied does not cause side effects.

相思碎 2024-07-29 05:35:30

您可以使用 map 来编写此代码,如下所示:

map(cls.my_func, items)

将 cls 替换为您要迭代的项目的类。

正如 Stephan202 所提到的,在这种情况下不建议这样做。

通常,如果您想通过对列表中的每个项目应用某些函数来创建新列表,请使用 map. 这意味着该函数没有副作用,因此您可以(可能)并行运行该映射。

如果您不想创建新列表,或者该函数有副作用,请使用 for 循环。 您的示例就是这种情况。

You can write this using map like this:

map(cls.my_func, items)

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.

萌酱 2024-07-29 05:35:30

存在轻微的语义差异,这可能在 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.

天涯沦落人 2024-07-29 05:35:30

如果需要,您可以将您的 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).

爱本泡沫多脆弱 2024-07-29 05:35:30

当您不需要返回结果列表(例如具有副作用的函数)时,请使用显式 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. ;)

总攻大人 2024-07-29 05:35:30

map 的主要优点是当您想要获取列表中每个元素的某些计算结果时。 例如,此代码片段将列表中的每个值加倍:

map(lambda x: x * 2, [1,2,3,4])  #=> [2, 4, 6, 8]

值得注意的是,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:

map(lambda x: x * 2, [1,2,3,4])  #=> [2, 4, 6, 8]

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 the for body to add the result of each calculation to the new list. The map version is more concise and functional.

空‖城人不在 2024-07-29 05:35:30

对于内置函数来说,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.

梦里人 2024-07-29 05:35:30

已经有很多好的答案了。 我想强调 map() 的另一个方面:代码可读性和透明度

特别是,我将重点关注问题的这一部分:

使用 map 与 for 是否有价值?


对我来说,在适当的情况下使用 map() 而不是循环或推导式,其主要好处是使代码更加透明。

(为简单起见,下面我假设我们正在 list 上进行操作,并且我假装 map() 返回一个 list [1])

对于阅读或浏览代码的人来说,使用 map() 转换的列表可以为读者提供额外的信息,而且成本非常低。 这些信息分为两类:

  • 强制由实现
  • 传达意图

使用map(),而不是循环或理解,

  • 它是强制的(我们确信):
    • 生成的列表与输入列表具有相同数量的元素
    • 输入列表未发生变化
  • 程序员传达了他们的意图:
    • 所应用的函数没有副作用

请注意,在 Python 社区中,通常认为使用推导式或 for 循环比 map() 更符合 Python 风格。

[1]:map() 的返回类型和惰性求值

map() 接受一个可迭代,并且返回一些可迭代的东西 -就像生成器的形式一样。

可迭代类型的示例是列表和元组。

您可以将返回值视为迭代器,但仅在需要时才会计算该值。 后者是 map() 实际上返回一个生成器的结果。 这称为惰性求值

我们可以用函数 prnt() 来说明该行为,该函数返回 None 并具有打印其副作用控制台的参数:

>>> def prnt(x): print(f'printing {x}'); return None
>>> a = prnt(1)
printing 1
>>> print(a)
None

现在,

# list comprehension:
>>> c = [prnt(x) for x in [1,2]]
printing 1
printing 2
>>> print(c)
[None, None]

# map:
>>> b = map(prnt, [1,2])
# no output - prints nothing!
>>> b  # what is it?
<map at 0x11241e290>
>>> l=list(b)  # converting to list forces evaluation
printing 1
printing 2
>>> l
[None, 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:

Is there a value in using map vs for?


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 that map() returns a list. [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:

  • enforced by the implementation
  • conveying intent

With map(), rather than a loop or comprehension,

  • it is enforced (we know for sure) that:
    • the produced list has the same number of elements as the input list
    • the input list is not mutated
  • the programmer conveyes their intent that:
    • the function that is applied has no side effects

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 evaluation

map() 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 returns None and has the side effect of printing its argument to the console:

>>> def prnt(x): print(f'printing {x}'); return None
>>> a = prnt(1)
printing 1
>>> print(a)
None

Now,

# list comprehension:
>>> c = [prnt(x) for x in [1,2]]
printing 1
printing 2
>>> print(c)
[None, None]

# map:
>>> b = map(prnt, [1,2])
# no output - prints nothing!
>>> b  # what is it?
<map at 0x11241e290>
>>> l=list(b)  # converting to list forces evaluation
printing 1
printing 2
>>> l
[None, None]
我不在是我 2024-07-29 05:35:30
map(lambda item: item.my_func(), items)
map(lambda item: item.my_func(), items)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文