986 Stimmen

Listenverständnis vs. Karte

Gibt es einen Grund, die Verwendung von map() über das Listenverständnis oder umgekehrt? Ist eine der beiden Methoden generell effizienter oder gilt sie als pythonischer als die andere?

18voto

Dan Punkte 1264

Ich finde, dass Listenzusammenfassungen im Allgemeinen besser ausdrücken, was ich zu tun versuche, als map - beides ist möglich, aber Ersteres erspart die geistige Belastung, die mit dem Verständnis eines möglicherweise komplexen Themas verbunden ist. lambda Ausdruck.

Es gibt auch irgendwo ein Interview (ich kann es nicht ausfindig machen), in dem Guido aufzählt lambda s und die funktionalen Funktionen als die Sache, die er am meisten bedauert, in Python zu akzeptieren, so könnte man das Argument, dass sie un-Python durch die Kraft, dass zu machen.

16voto

vishes_shell Punkte 19913

Seit Python 3, map() ein Iterator ist, müssen Sie sich überlegen, was Sie brauchen: einen Iterator oder list Objekt.

Wie @AlexMartelli bereits erwähnt , map() ist nur dann schneller als das Verstehen von Listen, wenn Sie nicht lambda Funktion.

Ich werde Ihnen einige Zeitvergleiche vorstellen.

Python 3.5.2 und CPython<br>Ich habe die <a href="https://ipython.org/notebook.html" rel="noreferrer">Jupiter Notizbuch </a>und insbesondere <a href="https://ipython.org/ipython-doc/3/interactive/magics.html#magic-timeit" rel="noreferrer"><code>%timeit</code></a> eingebauter magischer Befehl<br><strong>Messungen </strong>s == 1000 ms == 1000 1000 µs = 1000 1000 * 1000 ns

Einrichten:

x_list = [(i, i+1, i+2, i*2, i-9) for i in range(1000)]
i_list = list(range(1000))

Eingebaute Funktion:

%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

lambda Funktion:

%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

Es gibt auch so etwas wie einen Generatorausdruck, siehe PEP-0289 . Also dachte ich, es wäre nützlich, es zum Vergleich hinzuzufügen

%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

Sie benötigen list Objekt:

Listenverständnis verwenden, wenn es sich um eine benutzerdefinierte Funktion handelt, verwenden list(map()) wenn es eine eingebaute Funktion gibt

Sie brauchen keine list Objekt, Sie brauchen nur ein iterierbares:

Verwenden Sie immer map() !

2voto

craymichael Punkte 4126

Ich habe einen kurzen Test durchgeführt, in dem ich drei Methoden zum Aufrufen der Methode eines Objekts verglichen habe. Der Zeitunterschied ist in diesem Fall vernachlässigbar und hängt von der jeweiligen Funktion ab (siehe @Alex Martelli's Antwort ). Hier habe ich mir die folgenden Methoden angesehen:

# map_lambda
list(map(lambda x: x.add(), vals))

# map_operator
from operator import methodcaller
list(map(methodcaller("add"), vals))

# map_comprehension
[x.add() for x in vals]

Ich habe mir Listen angesehen (gespeichert in der Variablen vals ) der beiden Ganzzahlen (Python int ) und Gleitkommazahlen (Python float ) für zunehmende Listengrößen. Die folgende Dummy-Klasse DummyNum berücksichtigt wird:

class DummyNum(object):
    """Dummy class"""
    __slots__ = 'n',

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

    def add(self):
        self.n += 5

Genauer gesagt, die add Methode. Die Website __slots__ Attribut ist eine einfache Optimierung in Python, um den Gesamtspeicherbedarf der Klasse (Attribute) zu definieren und so die Speichergröße zu reduzieren. Hier sind die resultierenden Diagramme.

Performance of mapping Python object methods

Wie bereits erwähnt, macht die verwendete Technik nur einen minimalen Unterschied, und Sie sollten den Code so schreiben, wie er für Sie oder unter den gegebenen Umständen am besten lesbar ist. In diesem Fall wird das Listenverständnis ( map_comprehension Technik) ist für beide Arten von Hinzufügungen in einem Objekt am schnellsten, insbesondere bei kürzeren Listen.

Besuchen Sie dieses Pastebin für die Quelle, die zur Erstellung des Diagramms und der Daten verwendet wurde.

2voto

Nico Schlömer Punkte 45358

Ich habe einige der Ergebnisse mit Perfplot (ein Projekt von mir).

Wie andere bereits festgestellt haben, map gibt eigentlich nur einen Iterator zurück, so dass es sich um eine Operation mit konstanter Zeit handelt. Bei der Realisierung des Iterators durch list() ist sie gleichwertig mit den Listenauffassungen. Je nach Ausdruck könnte einer der beiden einen leichten Vorteil haben, aber der ist kaum signifikant.

Beachten Sie, dass arithmetische Operationen wie x ** 2 son viel schneller in NumPy, insbesondere wenn die Eingabedaten bereits ein NumPy-Array sind.

hex :

enter image description here

x ** 2 :

enter image description here


Code zum Reproduzieren der Diagramme:

import perfplot

def standalone_map(data):
    return map(hex, data)

def list_map(data):
    return list(map(hex, data))

def comprehension(data):
    return [hex(x) for x in data]

b = perfplot.bench(
    setup=lambda n: list(range(n)),
    kernels=[standalone_map, list_map, comprehension],
    n_range=[2 ** k for k in range(20)],
    equality_check=None,
)
b.save("out.png")
b.show()

import perfplot
import numpy as np

def standalone_map(data):
    return map(lambda x: x ** 2, data[0])

def list_map(data):
    return list(map(lambda x: x ** 2, data[0]))

def comprehension(data):
    return [x ** 2 for x in data[0]]

def numpy_asarray(data):
    return np.asarray(data[0]) ** 2

def numpy_direct(data):
    return data[1] ** 2

b = perfplot.bench(
    setup=lambda n: (list(range(n)), np.arange(n)),
    kernels=[standalone_map, list_map, comprehension, numpy_direct, numpy_asarray],
    n_range=[2 ** k for k in range(20)],
    equality_check=None,
)
b.save("out2.png")
b.show()

1voto

Mohit Raj Punkte 35

Ich habe den Code von @alex-martelli ausprobiert, aber einige Unstimmigkeiten gefunden

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

map benötigt selbst für sehr große Bereiche die gleiche Zeit, während die Verwendung von List Comprehension sehr viel Zeit in Anspruch nimmt, wie aus meinem Code ersichtlich ist. Abgesehen davon, dass ich als "unpythonisch" gelte, habe ich also keine Leistungsprobleme im Zusammenhang mit der Verwendung von map gehabt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X