d.setdefault(x, []).append(y)

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Wollte mal sehen, was schneller ist:

Code: Alles auswählen

def without_setdefault1():
    d = {}
    for x in xrange(500):
        for y in xrange(100):
            if x not in d:
                d[x] = [y]
            else:
                d[x].append(y)


def without_setdefault2():
    d = {}
    for x in xrange(500):
        for y in xrange(100):
            try:
                d[x].append(y)
            except KeyError:
                d[x] = [y]


def with_setdefault():
    d = {}
    for x in xrange(500):
        for y in xrange(100):
            d.setdefault(x, []).append(y)


if __name__ == '__main__':
    from timeit import Timer

    def timeit(func, number):
        name = func.__name__
        t = Timer("%s()" % name, "from __main__ import %s" % name)
        print "%s:" % name,
        print "%.3fsec" % t.timeit(number)

    number = 200
    timeit(with_setdefault, number)
    timeit(without_setdefault1, number)
    timeit(without_setdefault2, number)
Ergebnis:

Code: Alles auswählen

with_setdefault: 3.404sec
without_setdefault1: 2.462sec
without_setdefault2: 2.361sec
Ich denke ob nun without_setdefault1 oder without_setdefault2 schneller ist, kommt darauf an wie oft x schon in d vorhanden ist oder nicht.

Welche Variante nutzt ihr am meisten? Bisher nehme ich immer without_setdefault1

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Ich würde keine der drei Varianten nutzen, collections.defaultdict existiert.
Das Leben ist wie ein Tennisball.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Ah! Guter Einwand:

Code: Alles auswählen

import collections

def without_setdefault1():
    d = {}
    for x in xrange(500):
        for y in xrange(100):
            if x not in d:
                d[x] = [y]
            else:
                d[x].append(y)


def without_setdefault2():
    d = {}
    for x in xrange(500):
        for y in xrange(100):
            try:
                d[x].append(y)
            except KeyError:
                d[x] = [y]


def with_setdefault():
    d = {}
    for x in xrange(500):
        for y in xrange(100):
            d.setdefault(x, []).append(y)


def defaultdict():
    d = collections.defaultdict(list)
    for x in xrange(500):
        for y in xrange(100):
            d[x].append(y)


if __name__ == '__main__':
    from timeit import Timer

    def timeit(func, number):
        name = func.__name__
        t = Timer("%s()" % name, "from __main__ import %s" % name)
        print "%s:" % name,
        print "%.3fsec" % t.timeit(number)

    number = 200
    timeit(defaultdict, number)
    timeit(with_setdefault, number)
    timeit(without_setdefault1, number)
    timeit(without_setdefault2, number)
Ist auch die schnellste Variante:

Code: Alles auswählen

defaultdict: 1.910sec
with_setdefault: 3.355sec
without_setdefault1: 2.442sec
without_setdefault2: 2.346sec

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

tl;dr: Pythonischste Lösung ist die schnellste. Weitergehn, hier gibts nichts zu sehn ;)
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

und welche findest du die Pythonischste?

Ich favorisiere z.Z. collections.defaultdict()... dabei sieht die Iteration am saubersten aus.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

jens hat geschrieben:und welche findest du die Pythonischste?
Steht doch da, die schnellste, und das ist ``defaultdict``.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Entgegen früherer Praxis gehe ich inzwischen vermehrt zu `try:`-Konstrukten über. Zwar ist das IMHO nicht ganz so hübsch zu schreiben, aber eine Prüfung auf Vorhandensein z.B. eines Schlüssels im Wörterbuch findet ja bei der Abfrage des Schlüssels ohnehin statt, damit ggf eine Ausnahme geworfen werden kann. Wenn man das vorher schon selbst in Python-Code vorgenommen hat, dann findet diese Überprüfung also doppelt statt. Das kostet natürlich immer ein bißchen an zusätzlicher Zeit. Da ich weiß, dass es beim `try:`-Ansatz nur dann auf die Performance geht, wenn tatsächlich eine Exception geworfen wird (was ja buchstäblich die Ausnahme bzw der weniger häufige Fall sein sollte), kann das mitunter durchaus praktikabel sein. Es sei aber natürlich angemerkt (vor allem für mitlesende Anfänger), dass solcherlei Optimierungen *eigentlich* selten sinnvoll sind. Zudem bietet Python (wie hier im Thread schon gezeigt) oft auch optimierte eigene Objekte für bestimmte Anforderungen an (hier: `collections.defaultdict`).
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Außerdem entspricht die vorherige Prüfung nicht dem EAFP-Prinzip, welches in Python LBYL bevorzugt wird.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Ich nutze üblicherweise setdefault es sei den dass dict wird nur innerhalb einer Funktion/Methode benutzt und verlässt diesen Raum nicht.
Antworten