Angenommen, connectionDetails
ist ein Python-Wörterbuch. Was ist die beste, eleganteste und "pythonischste" Art, Code wie folgt zu überarbeiten?
if "Host" in connectionDetails:
Host = connectionDetails["Host"]
else:
Host = someDefaultValue
So was:
Host = connectionDetails.get('Host', someDefaultValue)
Sie können auch die defaultdict
wie folgt verwenden:
from collections import defaultdict
a = defaultdict(lambda: "default", key="some_value")
a["blabla"] => "default"
a["key"] => "some_value"
Sie können jede gewöhnliche Funktion anstelle von Lambda übergeben:
from collections import defaultdict
def a():
return 4
b = defaultdict(a, key="some_value")
b['absent'] => 4
b['key'] => "some_value"
Während .get()
ein Nizza-Idiom ist, ist es langsamer als if/else
(und langsamer als try/except
, wenn der Schlüssel meistens im Wörterbuch vorhanden ist):
>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
... stmt="try:\n a=d[1]\nexcept KeyError:\n a=10")
0.07691968797894333
>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
... stmt="try:\n a=d[2]\nexcept KeyError:\n a=10")
0.4583777282275605
>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
... stmt="a=d.get(1, 10)")
0.17784020746671558
>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
... stmt="a=d.get(2, 10)")
0.17952161730158878
>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
... stmt="if 1 in d:\n a=d[1]\nelse:\n a=10")
0.10071221458065338
>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
... stmt="if 2 in d:\n a=d[2]\nelse:\n a=10")
0.06966537335119938
Für mehrere verschiedene Standardeinstellungen versuchen Sie Folgendes:
connectionDetails = { "Host": "www.example.com" }
defaults = { "Host": "127.0.0.1", "port": 8080 }
completeDetails = {}
completeDetails.update(defaults)
completeDetails.update(connectionDetails)
completeDetails["Host"] # ==> "www.example.com"
completeDetails["port"] # ==> 8080
Es gibt eine Methode in Python-Wörterbüchern, um dies auszuführen: dict.setdefault
connectionDetails.setdefault('Host',someDefaultValue)
Host = connectionDetails['Host']
Diese Methode setzt jedoch den Wert von connectionDetails['Host']
auf someDefaultValue
, wenn der Schlüssel Host
nicht bereits definiert ist, anders als bei der Frage.
(Dies ist eine späte Antwort)
Eine Alternative ist die Unterklasse der Klasse dict
und die Implementierung der Methode __missing__()
wie folgt:
class ConnectionDetails(dict):
def __missing__(self, key):
if key == 'Host':
return "localhost"
raise KeyError(key)
Beispiele:
>>> connection_details = ConnectionDetails(port=80)
>>> connection_details['Host']
'localhost'
>>> connection_details['port']
80
>>> connection_details['password']
Traceback (most recent call last):
File "python", line 1, in <module>
File "python", line 6, in __missing__
KeyError: 'password'
Beim Testen von @Tim Pietzckers Verdacht über die Situation in PyPy (5.2.0-alpha0) für Python 3.3.5 stelle ich fest, dass in der Tat sowohl .get()
als auch die if
/else
-Methode ähnlich sind. Es scheint tatsächlich so zu sein, dass es im if/else-Fall sogar nur eine einzige Suche gibt, wenn die Bedingung und die Zuweisung den gleichen Schlüssel beinhalten (vergleiche mit dem letzten Fall, bei dem zwei Suchen vorhanden sind).
>>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
.... stmt="try:\n a=d[1]\nexcept KeyError:\n a=10")
0.011889292989508249
>>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
.... stmt="try:\n a=d[2]\nexcept KeyError:\n a=10")
0.07310474599944428
>>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
.... stmt="a=d.get(1, 10)")
0.010391917996457778
>>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
.... stmt="a=d.get(2, 10)")
0.009348208011942916
>>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
.... stmt="if 1 in d:\n a=d[1]\nelse:\n a=10")
0.011475925013655797
>>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
.... stmt="if 2 in d:\n a=d[2]\nelse:\n a=10")
0.009605801998986863
>>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
.... stmt="if 2 in d:\n a=d[2]\nelse:\n a=d[1]")
0.017342638995614834
Sie können hierfür eine Lamba-Funktion als Einzeiler verwenden. Erstellen Sie ein neues Objekt connectionDetails2
, auf das wie eine Funktion zugegriffen wird ...
connectionDetails2 = lambda k: connectionDetails[k] if k in connectionDetails.keys() else "DEFAULT"
Jetzt benutzen
connectionDetails2(k)
anstatt
connectionDetails[k]
gibt den Wörterbuchwert zurück, wenn sich k
in den Schlüsseln befindet, andernfalls "DEFAULT"
.