Seite 1 von 2

Frage zu Umgang mit get()

Verfasst: Montag 9. August 2021, 11:38
von YAPD
Hallo Zusammen,

ich habe mal eine Frage zur dem Ermitteln von Einträgen aus Dictionaries.
Wie würdet ihr diese am Besten ermitteln, bzw. welchen Code würdet ihr
bevorzugen ?

Code: Alles auswählen

import re
from contextlib import suppress

examples = {"Parameters": {"Eins": "One", "Zwei": "Two", "Drei": "Three"}}

# Example I 

print("Eins :", examples["Parameters"][ "Eins" ] )

from contextlib import suppress

# Example II

with suppress(KeyError):
    print("Zwei :", examples["Parameters"].get("Zwei"))

# Example III

try:
    print("Drei :", examples["Parameters"].get("Drei"))
except KeyError:
    print("The key is not existing !")
Tor 1 , 2 oder 3 ? :)

VG
YAPD

Re: Frage zu Umgang mit get()

Verfasst: Montag 9. August 2021, 11:54
von darktrym
Nr.1, get verwende ich nur wenn auch Default value vorkommen kann.

Re: Frage zu Umgang mit get()

Verfasst: Montag 9. August 2021, 11:56
von nezzcarth
Den 'suppress'-Kontextmanager habe ich glaube ich selbst noch nie verwendet. Das Gute an 'get' ist, dass es einen zweiten Parameter erhalten kann, der zurückgegeben wird, wenn der Key nicht enthalten ist. Das ist standardmäßig 'None', kann aber zum Beispiel auch eine leere Liste sein. Das bietet sich also oft in den Fällen an, in denen man den Wert bei Nicht-Existenz auf irgendeinen Default setzen möchte, um damit weiterarbeiten zu können. Die Variante mit direktem Dictionary-Zugriff (also ohne get) und anschließender Exception ist in manchen Situationen eine Alternative zu 'get', und bietet sich ansonsten in Fällen wie dem Gezeigten an, in denen am z.B. einfach nur eine Fehlermeldung ausgeben möchte.

Re: Frage zu Umgang mit get()

Verfasst: Montag 9. August 2021, 12:07
von rogerb
@YAPD,

[Edit] Ich hatte mich wohl verlesen.
Beispiel 1: Ja, genau so
Wenn ich im Normalfall keinen KeyError erwarte, würde ich den überhaupt nicht behandeln.
Wenn dann aber doch mal ein Fehler auftritt soll er mir auch gleich krachend um die Ohren fliegen.
Exceptionhandling ist ja dazu gedacht, dass man einen solchen Fehlerfall durchaus für möglich hält und dafür auch eine sinnvolle Alternative hat.

Beispiel 2: Nein, weil ich keine Notwendigkeit für suppress sehe
Beispiel 3: Nein, entweder get() mit default value, oder wenn es ein komplexerer Fall ist, ohne get() aber mit KeyError handling

Re: Frage zu Umgang mit get()

Verfasst: Montag 9. August 2021, 14:03
von YAPD
nezzcarth hat geschrieben: Montag 9. August 2021, 11:56 Den 'suppress'-Kontextmanager habe ich glaube ich selbst noch nie verwendet. Das Gute an 'get' ist, dass es einen zweiten Parameter erhalten kann, der zurückgegeben wird, wenn der Key nicht enthalten ist. Das ist standardmäßig 'None', kann aber zum Beispiel auch eine leere Liste sein. Das bietet sich also oft in den Fällen an, in denen man den Wert bei Nicht-Existenz auf irgendeinen Default setzen möchte, um damit weiterarbeiten zu können. Die Variante mit direktem Dictionary-Zugriff (also ohne get) und anschließender Exception ist in manchen Situationen eine Alternative zu 'get', und bietet sich ansonsten in Fällen wie dem Gezeigten an, in denen am z.B. einfach nur eine Fehlermeldung ausgeben möchte.
Danke für deine Antwort. Das stimmt allerdings, klappt aber nur mit eindimensionalen Dics. Sobald der erste der beiden Keys nicht existiert, wird
eine KeyError Meldung ausgelöst. Deshalb die Möglichkeit mit suppress. Allerdings wird die Variable dann gar nicht angezeigt, ohne Fehlermeldung.
rogerb hat geschrieben: Montag 9. August 2021, 12:07 @YAPD,

[Edit] Ich hatte mich wohl verlesen.
Beispiel 1: Ja, genau so
Wenn ich im Normalfall keinen KeyError erwarte, würde ich den überhaupt nicht behandeln.
Wenn dann aber doch mal ein Fehler auftritt soll er mir auch gleich krachend um die Ohren fliegen.
Exceptionhandling ist ja dazu gedacht, dass man einen solchen Fehlerfall durchaus für möglich hält und dafür auch eine sinnvolle Alternative hat.

Beispiel 2: Nein, weil ich keine Notwendigkeit für suppress sehe
Beispiel 3: Nein, entweder get() mit default value, oder wenn es ein komplexerer Fall ist, ohne get() aber mit KeyError handling
Danke für deine Antwort. Ich denke, auch dass die 1. ( Standard ) Lösung am Besten ist, es gibt
aber, denke ich, schon Situation, in denen man die try bzw. suppress() Lösung verwenden kann.

VG
YAPD

Re: Frage zu Umgang mit get()

Verfasst: Montag 9. August 2021, 14:24
von kbr
"try/except" und "get" sind sinnvoll, wenn es vorkommen kann, dass ein Key nicht existiert. Ein bisschen ist es ein Stilfrage, welche Form bevorzugt werden soll. Falls es aber eine Laufzeitfrage sein sollte, dann ist es gut zu wissen, dass ein "try" preiswerter ist als ein "get", ein "except" aber deutlich teurer. D.h. "try/except" ist von Vorteil, wenn wenig KeyErrors erwartet werden, ansonsten kann "get" zu bevorzugen sein. Um den Unterschied zu bemerken, bedarf es aber einer starken Frequentierung. Für "suppress" habe ich noch keine Anwendung gefunden. Im Gegenteil – ich möchte gar keine Exceptions unterdrücken.

Re: Frage zu Umgang mit get()

Verfasst: Montag 9. August 2021, 14:29
von YAPD
kbr hat geschrieben: Montag 9. August 2021, 14:24 "try/except" und "get" sind sinnvoll, wenn es vorkommen kann, dass ein Key nicht existiert. Ein bisschen ist es ein Stilfrage, welche Form bevorzugt werden soll. Falls es aber eine Laufzeitfrage sein sollte, dann ist es gut zu wissen, dass ein "try" preiswerter ist als ein "get", ein "except" aber deutlich teurer. D.h. "try/except" ist von Vorteil, wenn wenig KeyErrors erwartet werden, ansonsten kann "get" zu bevorzugen sein. Um den Unterschied zu bemerken, bedarf es aber einer starken Frequentierung. Für "suppress" habe ich noch keine Anwendung gefunden. Im Gegenteil – ich möchte gar keine Exceptions unterdrücken.
Hi kbr,

vielen Dank für deine Antwort. Eigentlich haben mich die Unterschiede interessiert, aber ist wirklich gut zu wissen, denn
den Code möglichst performant zu schreiben, ist ja einer der wichtigsten Dinge beim Programmieren. Aber noch eine
Frage. Wieso sollte man einen try ohne except machen ? D.h. also eigentlich sind gets( ) immer guenstiger als trys /
excepts ?

VG
YAPD

Re: Frage zu Umgang mit get()

Verfasst: Montag 9. August 2021, 15:14
von kbr
@YAPD: es gibt kein try ohne except. Der except branch wird aber nur ausgeführt, wenn ein KeyError auftritt. Wenn keiner auftritt, ist get teurer. Das wirst Du in der Praxis aber erst merken, wenn innerhalb kurzer Zeit extrem viele Zugriffe auf ein Dictionary erfolgen. Es zwar gut, performanten Code zu schreiben, aber noch wichtiger ist zu wissen, wann das wirklich erforderlich ist. Erster Ansatzpunkt ist hier in der Regel der Algorithmus und nicht Details wie try und get.

Re: Frage zu Umgang mit get()

Verfasst: Montag 9. August 2021, 17:08
von YAPD
kbr hat geschrieben: Montag 9. August 2021, 15:14 @YAPD: es gibt kein try ohne except. Der except branch wird aber nur ausgeführt, wenn ein KeyError auftritt. Wenn keiner auftritt, ist get teurer. Das wirst Du in der Praxis aber erst merken, wenn innerhalb kurzer Zeit extrem viele Zugriffe auf ein Dictionary erfolgen. Es zwar gut, performanten Code zu schreiben, aber noch wichtiger ist zu wissen, wann das wirklich erforderlich ist. Erster Ansatzpunkt ist hier in der Regel der Algorithmus und nicht Details wie try und get.
Wo wir gerade bei Performance sind, kannst du mir erklären warum ich bei Vergleich der Geschwindigkeit
folgender ( identischen ) Codes bei beiden Werten die gleiche Zeit erhalte :

Code: Alles auswählen

from collections import Counter
from collections import defaultdict
import time
from datetime import datetime

portfolio = [
    ('GOOG', 100, 490.1),
    ('IBM', 50, 91.1),
    ('CAT', 150, 83.44),
    ('IBM', 100, 45.23),
    ('GOOG', 75, 572.45),
    ('AA', 50, 23.15)
]

total_shares = Counter()
Shares = {}

for name, shares, price in portfolio:
    total_shares[name] += shares

startTime = datetime.now()


def V1():

    for rowno, row in enumerate(portfolio, start=0):

        if row[0] in Shares:
            Shares[row[0]].append((row[1], row[2]))
        else:
            Shares[row[0]] = [(row[1], row[2])]

    cpu_time = time.process_time()
    print("CPU / Processing Time : ", cpu_time)

    print(datetime.now() - startTime)


def V2():

    holdings = defaultdict(list)

    for name, shares, price in portfolio:
        holdings[name].append((shares, price))

    cpu_time= time.process_time()
    print("CPU / Processing Time : ", cpu_time)

    print(datetime.now() - startTime)
Der Code in V1 macht exakt das selbe, nur ohne "defaultdict( )", natürlich weiß ich,
dass es besser "defaultdict( )" zu verwenden. Daher auch meine Frage. Warum sind
beide Codes gleich schnell, oder mache ich was falsch ?

Code: Alles auswählen

V1 : CPU / Processing Time :  0.046875
V1 : 0:00:00

V2 : CPU / Processing Time :  0.046875
V2 : 0:00:00
VG
YAPD

Re: Frage zu Umgang mit get()

Verfasst: Montag 9. August 2021, 18:34
von Sirius3
Deine Zeitmessung ist sehr ungenau, von daher sind die minimalen Unterschiede nicht sichtbar.
Du misst hauptsächlich die Startup-Zeit.

@kbr: einem try-Block kann auch ein finally-Block folgen, ohne except-Block.

Re: Frage zu Umgang mit get()

Verfasst: Montag 9. August 2021, 19:07
von kbr
@Sirius3: ja sicher. Oder mehrere excepts und auch noch ein else. Ein try/finally ist mit einem get aber nicht mehr vergleichbar, da die Exception weitergereicht wird.

Re: Frage zu Umgang mit get()

Verfasst: Montag 9. August 2021, 19:19
von sparrow
@YAPD: Deine Variante 1 ist ein gutes Beispiel für schlecht lesbaren Code. Bevor du anfängst über Geschwindigkeitsoptiomierungen nachzudenken, solltest du verständlichen Code zu schreiben. Und falls du irgendwann mal ein Geschwindigkeitsproblem hast, dann kannst du die Optimierung angehen.

Variablennamen schreibt man klein_mit_unterstrich. Klassennamen (nicht die Namen der Instanzen) schreibt man PascalCase.
Du verwendest bei Variante 1 enumerate, brauchst aber den Index gar nicht. Außerdem verwendest du da "magische Index-Zugriffe", die man erst mühsam nachvollziehen muss. In Variante 2 machst du das besser, da bindest du die Elemente an Namen.
Vernünftige Namen sind übrigens meist sehr hilfreich.

Re: Frage zu Umgang mit get()

Verfasst: Montag 9. August 2021, 20:02
von __blackjack__
@YAPD: Für solche Messungen sollte man das `timeit`-Modul verwenden. Das umschifft diverse Probleme die auftreten können, damit meinen keinen, oder weniger Mist misst. 🙂

Re: Frage zu Umgang mit get()

Verfasst: Dienstag 10. August 2021, 14:19
von YAPD
Hi Zusammen,

vielen Dank für alle Eure Antworten. Dass ich den o.g. Code gepostet habe, hat den Hintergrund, dass ich gerade noch
am Lernen bin und mir defaultdict angeschaut habe. Ich habe dann zu Übungszwecken versucht, den Code abzubilden,
nur um zu verstehen, was es macht. ( Mich interessiert immer auch der Hintergrund ). Ich weiß, dass man Variablen
immer klein schreibt, bei "Counter" und "Shares" hab ich es übersehen, Flüchtigkeitsfehler :)

VG
YAPD

Re: Frage zu Umgang mit get()

Verfasst: Dienstag 10. August 2021, 18:36
von DasIch
Ohne mit die Implementierung von defaultdict angeschaut zu haben, würde ich mal stark davon ausgehen dass die auf __missing__ aufbaut. Dementsprechend wird weder KeyError noch .get() involviert sein.

Re: Frage zu Umgang mit get()

Verfasst: Dienstag 10. August 2021, 22:55
von rogerb
@DasIch,
Dementsprechend wird weder KeyError noch .get() involviert sein.
Doch, DefaultDict implementiert __missing__ () was bei einem fehlenden Key einen KeyError wirft wenn keine factory Function angegeben wurde.
get() gibt es auch und verhält sich so wie bei einem Standard-Dictionary.

Re: Frage zu Umgang mit get()

Verfasst: Mittwoch 11. August 2021, 05:43
von sparrow
@rogerb: "involviert" ist nicht "implementiert"

Re: Frage zu Umgang mit get()

Verfasst: Mittwoch 11. August 2021, 07:27
von rogerb
@sparrow,
"involviert" ist nicht "implementiert"
Und was willst du damit sagen? Was soll "involviert" eigentlich bedeuten?

Der zentrale Punkt ist, dass es auch bei defaultdict eine KeyError Exception geben kann und das man die get() Methode auch mit einem defaultdict verwenden kann.

Re: Frage zu Umgang mit get()

Verfasst: Mittwoch 11. August 2021, 09:04
von __blackjack__
@rogerb: Aber wann gibt es denn einen `KeyError`? Doch nur wenn man ein `defaultdict` falsch benutzt, weil man es ohne Factory-Funktion erstellt hat, und damit eigentlich gar kein `defaultdict` bräuchte, sondern einfach ein normales Wörterbuch hätte nehmen können/sollen.

Klar gibt es auch eine `get()`-Methode, weil ein `defaultdict` auch überall da funktionieren sollte, wo ein `dict` erwartet wird, und da kann dann natürlich auch ein `get()`-Aufruf stehen. Aber in eigenem Code bzw. Code der *weiss* das es sich speziell um ein `defaultdict` handelt, macht `get()` keinen Sinn. Oder nur, wenn man vorher einen sinnvollen Grund gefunden hat `defaultdict` ohne Fabrikfunktion aufzurufen.

Re: Frage zu Umgang mit get()

Verfasst: Mittwoch 11. August 2021, 09:17
von rogerb
@__blackjack__,
du hast jetzt nochmal ausführlich wiederholt, was ich schon gesagt hatte. - Danke, aber ... ?