Mam pytanie dotyczące idiomów i czytelności i wydaje się, że w tym konkretnym przypadku dochodzi do zderzenia filozofii Pythona:
Chcę zbudować słownik A ze słownika B. Jeśli określony klucz nie istnieje w B, nie rób nic i kontynuuj.
Który sposób jest lepszy?
try:
A["blah"] = B["blah"]
except KeyError:
pass
lub
if "blah" in B:
A["blah"] = B["blah"]
„Czyń i proś o przebaczenie” kontra „prostota i jednoznaczność”.
Który jest lepszy i dlaczego?
python
idioms
readability
defaultdict
code-readability
LeeMobile
źródło
źródło
if "blah" in B.keys()
, lubif B.has_key("blah")
.A.update(B)
nie pracować dla Ciebie?has_key
został wycofany na korzyśćin
i sprawdzanieB.keys()
zmienia operację O (1) na operację O (n)..has_key
jest przestarzały ikeys
tworzy niepotrzebną listę w py2k i jest zbędny w py3kA = dict((k, v) for (k, v) in B if we_want_to_include(k))
.Odpowiedzi:
Wyjątki nie są warunkami warunkowymi.
Wersja warunkowa jest jaśniejsza. To naturalne: jest to prosta kontrola przepływu, do której służą warunki warunkowe, a nie wyjątki.
Wersja wyjątku jest używana przede wszystkim jako optymalizacja podczas wykonywania tych wyszukiwań w pętli: w przypadku niektórych algorytmów pozwala na wyeliminowanie testów z wewnętrznych pętli. Tutaj nie ma takiej korzyści. Ma tę małą zaletę, że unika konieczności
"blah"
dwukrotnego mówienia , ale jeśli robisz wiele z tych rzeczy, i tak prawdopodobnie powinieneś mieć funkcję pomocnicząmove_key
.Ogólnie rzecz biorąc, zdecydowanie zalecam domyślne trzymanie się wersji warunkowej, chyba że masz konkretny powód, aby tego nie robić. Warunki warunkowe są oczywistym sposobem, aby to zrobić, co zwykle jest silną rekomendacją, aby preferować jedno rozwiązanie od innego.
źródło
"blah"
częściej, co prowadzi do sytuacji bardziej podatnej na błędy.Istnieje również trzeci sposób, który pozwala uniknąć zarówno wyjątków, jak i podwójnego wyszukiwania, co może być ważne, jeśli wyszukiwanie jest drogie:
value = B.get("blah", None) if value is not None: A["blah"] = value
Jeśli spodziewasz się, że słownik będzie zawierał
None
wartości, możesz użyć bardziej ezoterycznych stałych, takich jakNotImplemented
,Ellipsis
lub utworzyć nową:MyConst = object() def update_key(A, B, key): value = B.get(key, MyConst) if value is not MyConst: A[key] = value
W każdym razie użycie
update()
jest dla mnie najbardziej czytelną opcją:a.update((k, b[k]) for k in ("foo", "bar", "blah") if k in b)
źródło
Z tego, co rozumiem, chcesz zaktualizować dyktę A za pomocą par klucza i wartości z dyktu B.
update
to lepszy wybór.Przykład:
>>> A = {'a':1, 'b': 2, 'c':3} >>> B = {'d': 2, 'b':5, 'c': 4} >>> A.update(B) >>> A {'a': 1, 'c': 4, 'b': 5, 'd': 2} >>>
źródło
A.update({k: v for k, v in B.iteritems() if k in specificset})
Bezpośredni cytat z wiki wydajności Pythona:
Wydaje się więc, że obie opcje są wykonalne w zależności od sytuacji. Aby uzyskać więcej informacji, możesz sprawdzić ten link: Spróbuj z wyjątkiem wydajności
źródło
Myślę, że ogólna zasada tutaj jest
A["blah"]
normalna, jeśli tak, spróbuj-z wyjątkiem tego, że jest dobre, jeśli nie, to użyjif "blah" in b:
Myślę, że „spróbowanie” jest tanie w czasie, ale „z wyjątkiem” jest droższe.
źródło
Myślę, że drugi przykład jest tym, do czego powinieneś się udać, chyba że ten kod ma sens:
try: A["foo"] = B["foo"] A["bar"] = B["bar"] A["baz"] = B["baz"] except KeyError: pass
Pamiętaj, że kod zostanie przerwany, gdy tylko pojawi się klucz, którego nie ma
B
. Jeśli ten kod ma sens, powinieneś użyć metody wyjątku, w przeciwnym razie użyj metody testowej. Moim zdaniem, ponieważ jest krótszy i wyraźnie wyraża zamiar, jest o wiele łatwiejszy do odczytania niż metoda wyjątku.Oczywiście ludzie, którzy mówią, że masz używać,
update
mają rację. Jeśli używasz wersji Pythona, która obsługuje wyrażenia słownikowe, zdecydowanie wolałbym ten kod:updateset = {'foo', 'bar', 'baz'} A.update({k: B[k] for k in updateset if k in B})
źródło
for key in ["foo", "bar", "baz"]: try: A[key] = B[key]
Zasadą w innych językach jest zastrzeżenie wyjątków dla wyjątkowych warunków, tj. Błędów, które nie występują podczas regularnego używania. Nie wiem, jak ta reguła odnosi się do Pythona, ponieważ StopIteration nie powinien istnieć przez tę regułę.
źródło
Osobiście skłaniam się ku drugiej metodzie (ale używam
has_key
):if B.has_key("blah"): A["blah"] = B["blah"]
W ten sposób każda operacja przypisania ma tylko dwie linie (zamiast 4 z try / except), a wszelkie wyjątki, które zostaną wyrzucone, będą prawdziwymi błędami lub rzeczami, które przegapiłeś (zamiast próbować tylko uzyskać dostęp do kluczy, których tam nie ma) .
Jak się okazuje (zobacz komentarze do twojego pytania),
has_key
jest przestarzały - więc myślę, że lepiej napisać jakoif "blah" in B: A["blah"] = B["blah"]
źródło
Rozpoczynając
Python 3.8
i wprowadzając wyrażenia przypisania (PEP 572) (:=
operator), możemy przechwycić wartość warunkudictB.get('hello', None)
w zmiennejvalue
, aby zarówno sprawdzić, czy tak nie jestNone
(ponieważdict.get('hello', None)
zwraca skojarzoną wartość lubNone
), a następnie użyć jej w treści warunek:# dictB = {'hello': 5, 'world': 42} # dictA = {} if value := dictB.get('hello', None): dictA["hello"] = value # dictA is now {'hello': 5}
źródło
Chociaż w przyjętej odpowiedzi podkreślenie zasady „zobacz, zanim skoczysz” może mieć zastosowanie do większości języków, bardziej Pythonowe może być pierwszym podejściem, opartym na zasadach Pythona. Nie wspominając o tym, że jest to legalny styl kodowania w Pythonie. Ważne jest, aby upewnić się, że używasz try except block w odpowiednim kontekście i postępujesz zgodnie z najlepszymi praktykami. Na przykład. robienie zbyt wielu rzeczy w bloku try, przechwytywanie bardzo szerokiego wyjątku lub, co gorsza, klauzula gołego wyjątkiem itp.
Zobacz dokumentację Pythona tutaj .
Także to blog Bretta, jednego z głównych twórców, porusza większość tego w skrócie.
Zobacz inną dyskusję SO tutaj :
źródło