Odpowiedzi:
Lubię to:
host = connectionDetails.get('host', someDefaultValue)
if/else
jest znacznie szybszy. To może, ale nie musi, odgrywać pewną rolę.
if/else
jest szybszy?
Możesz także użyć defaultdict
podobnego:
from collections import defaultdict
a = defaultdict(lambda: "default", key="some_value")
a["blabla"] => "default"
a["key"] => "some_value"
Możesz przekazać dowolną zwykłą funkcję zamiast lambda:
from collections import defaultdict
def a():
return 4
b = defaultdict(a, key="some_value")
b['absent'] => 4
b['key'] => "some_value"
get
ani podobnych metod.
Chociaż .get()
jest ładnym idiomem, jest wolniejszy niż if/else
(i wolniejszy niż, try/except
jeśli przez większość czasu można oczekiwać obecności klucza w słowniku):
>>> 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
if/then
byłoby szybciej. Oba przypadki wymagają przeszukiwania słownika, a jeśli wywoływanie get()
jest o wiele wolniejsze, co jeszcze tłumaczy spowolnienie?
O(1)
niezależne od wielkości słownika, więc narzut związany z wywołaniem funkcji jest istotny.
W przypadku wielu różnych ustawień domyślnych spróbuj tego:
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
None
parametr connectionDetails lub parametr emptyString jako jedna z wartości w parach klucz-wartość. W defaults
słowniku potencjalnie jedna z jego wartości może zostać przypadkowo wygaszona. (patrz także stackoverflow.com/questions/6354436 )
W słownikach Pythona istnieje taka metoda: dict.setdefault
connectionDetails.setdefault('host',someDefaultValue)
host = connectionDetails['host']
Jednak metoda ta ustawia wartość connectionDetails['host']
, aby someDefaultValue
jeśli klucz host
nie jest już zdefiniowane, w przeciwieństwie do tego, co zadał pytanie.
setdefault()
wartość zwrotów, tak to działa, jak również: host = connectionDetails.setdefault('host', someDefaultValue)
. Tylko uważaj, że ustawi connectionDetails['host']
wartość domyślną, jeśli klucz nie był wcześniej.
(to późna odpowiedź)
Alternatywą jest podklasę dict
klasy i implementację __missing__()
metody w następujący sposób:
class ConnectionDetails(dict):
def __missing__(self, key):
if key == 'host':
return "localhost"
raise KeyError(key)
Przykłady:
>>> 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'
Testując podejrzenia @ Tima Pietzckera dotyczące sytuacji w PyPy (5.2.0-alpha0) dla Pythona 3.3.5, stwierdzam, że rzeczywiście oba sposoby .get()
i if
/ i else
działają podobnie. Właściwie wydaje się, że w przypadku if / else jest nawet tylko jedno wyszukiwanie, jeśli warunek i przypisanie dotyczą tego samego klucza (porównaj z ostatnim przypadkiem, w którym są dwa wyszukiwania).
>>>> 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
Możesz użyć do tego funkcji Lamba jako jednej linijki. Utwórz nowy obiekt, do connectionDetails2
którego dostęp jest uzyskiwany jak funkcja ...
connectionDetails2 = lambda k: connectionDetails[k] if k in connectionDetails.keys() else "DEFAULT"
Teraz użyj
connectionDetails2(k)
zamiast
connectionDetails[k]
która zwraca wartość słownika, jeśli k
jest w kluczach, w przeciwnym razie zwraca"DEFAULT"