`if key in dict` ile` try / exclude` - hangisi daha okunaklı deyim?


96

Deyimler ve okunabilirlik hakkında bir sorum var ve bu özel durum için Python felsefeleri çatışması var gibi görünüyor:

Sözlük B'den sözlük A oluşturmak istiyorum. B'de belirli bir anahtar yoksa, hiçbir şey yapmayın ve devam edin.

Hangi yol daha iyi?

try:
    A["blah"] = B["blah"]
except KeyError:
    pass

veya

if "blah" in B:
    A["blah"] = B["blah"]

"Af dileyin ve yapın" ile "basitlik ve açıklık".

Hangisi daha iyi ve neden?


1
İkinci örnek if "blah" in B.keys(), veya şeklinde daha iyi yazılabilir if B.has_key("blah").
girasquid

2
A.update(B)senin için çalışmıyor
SilentGhost

21
@Luke: Kullanımdan has_keykaldırıldı inve B.keys()bir O (1) işlemini bir O (n) bire dönüştürdüğünü kontrol etmek .
biraz

4
@Luke: değil değil. .has_keykullanımdan kaldırıldı ve keyspy2k'de gereksiz bir liste oluşturdu ve py3k'de gereksiz
SilentGhost

2
'inşa' A, olduğu gibi, A başlamak için boş mu? Ve biz sadece belirli anahtarları mı istiyoruz? Bir anlama kullanın: A = dict((k, v) for (k, v) in B if we_want_to_include(k)).
Karl Knechtel

Yanıtlar:


77

İstisnalar şart değildir.

Koşullu versiyon daha net. Bu doğal: bu, istisnalar için değil koşulların tasarlandığı basit akış kontrolüdür.

İstisna sürümü, öncelikle bu aramaları bir döngüde yaparken bir optimizasyon olarak kullanılır: bazı algoritmalar için, iç döngülerin testlerinin kaldırılmasına izin verir. Burada o yararı yok. "blah"İki kez söylemek zorunda kalmaması gibi küçük bir avantajı var , ancak bunların çoğunu yapıyorsanız muhtemelen move_keyyine de yardımcı bir işleve sahip olmalısınız .

Genel olarak, belirli bir nedeniniz olmadıkça, varsayılan olarak koşullu sürüme bağlı kalmanızı şiddetle tavsiye ederim. Koşullu ifadeler bunu yapmanın en açık yoludur ve bu genellikle bir çözümü diğerine tercih etmek için güçlü bir öneridir.


3
Ben katılmıyorum "X yap ve bu işe yaramazsa Y yap" dersen. Buradaki koşullu çözüme karşı ana sebep, "blah"daha sık yazmanız gerekiyor , bu da daha hataya açık bir duruma yol açıyor.
glglgl

6
Ve özellikle Python'da EAFP çok yaygın olarak kullanılmaktadır.
glglgl

8
Bu cevap Python dışında bildiğim herhangi bir dil için doğru olacaktır.
Tomáš Zato - Monica'yı eski durumuna döndür

3
İstisnaları Python'da koşullu gibi kullanıyorsanız, umarım kimsenin okuması gerekmez.
Glenn Maynard

Peki nihai karar nedir? :)
floatingpurr

63

Ayrıca, hem istisnaları hem de çift aramayı engelleyen üçüncü bir yol vardır; bu, arama pahalıysa önemli olabilir:

value = B.get("blah", None)
if value is not None: 
    A["blah"] = value

Eğer Sözlük içermesini bekliyoruz Nonesizin gibi biraz daha ezoterik sabitleri kullanabilirsiniz değerleri NotImplemented, Ellipsisya da yeni bir olun:

MyConst = object()
def update_key(A, B, key):
    value = B.get(key, MyConst)
    if value is not MyConst: 
        A[key] = value

Her neyse, kullanmak update()benim için en okunaklı seçenek:

a.update((k, b[k]) for k in ("foo", "bar", "blah") if k in b)

14

Anladığım kadarıyla, dikte A'yı anahtar, dikt B'deki değer çiftleriyle güncellemek istiyorsunuz

update daha iyi bir seçimdir.

A.update(B)

Misal:

>>> 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}
>>> 

"B'de belirli bir anahtar yoksa" Üzgünüm, daha açık olmalıydı, ancak yalnızca B'de belirli anahtarlar varsa değerlerin üzerine kopyalamak istiyorum. Hepsi B'de değil
LeeMobile

1
@LeeMobile -A.update({k: v for k, v in B.iteritems() if k in specificset})
Omnifarious

8

Python performans wiki'sinden doğrudan alıntı:

İlk sefer dışında, her kelime görüldüğünde if ifadesinin testi başarısız olur. Çok sayıda kelime sayıyorsanız, çoğu muhtemelen birden çok kez geçecektir. Bir değerin başlatılmasının yalnızca bir kez gerçekleşeceği ve bu değerin artırılmasının birçok kez gerçekleşeceği bir durumda, try deyimini kullanmak daha ucuzdur.

Dolayısıyla, duruma bağlı olarak her iki seçeneğin de geçerli olduğu görülüyor. Daha fazla ayrıntı için bu bağlantıya göz atmak isteyebilirsiniz: Performans dışında deneyin


bu ilginç bir okuma, ama bence biraz eksik. Kullanılan diktede yalnızca 1 öğe var ve daha büyük
kuralların

3

Sanırım buradaki genel kural A["blah"]normalde var olacak , öyleyse dene - hariç, değilse iyi olurif "blah" in b:

Bence "denemek" zaman açısından ucuz ama "hariç" daha pahalı.


10
Varsayılan olarak koda optimizasyon perspektifinden yaklaşmayın; ona okunabilirlik ve sürdürülebilirlik perspektifinden yaklaşın. Hedef özel olarak optimizasyon olmadığı sürece, bu yanlış kriterlerdir (ve optimizasyon ise, cevap tahmin etmektir, kıyaslamadır).
Glenn Maynard

Muhtemelen son noktayı parantez içine ya da bir şekilde belirsiz koymalıydım - asıl amacım birinciydi ve sanırım ikincisinin ek bir avantajı var.
neil

3

Sanırım ikinci örnek, bu kod mantıklı değilse gitmeniz gereken şey:

try:
    A["foo"] = B["foo"]
    A["bar"] = B["bar"]
    A["baz"] = B["baz"]
except KeyError:
    pass

İçinde olmayan bir anahtar olduğu anda kodun iptal edeceğini unutmayın B. Bu kod mantıklıysa, istisna yöntemini, aksi takdirde test yöntemini kullanmalısınız. Kanımca, daha kısa olduğu ve amacı açıkça ifade ettiği için, istisna yönteminden çok daha kolay okunur.

Tabii ki, kullanmanı söyleyenler updatedoğrudur. Sözlük anlamalarını destekleyen bir Python sürümü kullanıyorsanız, şu kodu kesinlikle tercih ederim:

updateset = {'foo', 'bar', 'baz'}
A.update({k: B[k] for k in updateset if k in B})

"B'de olmayan bir anahtar olduğu anda kodun iptal edileceğini unutmayın." - bu nedenle en iyi uygulama, try: block'ta yalnızca mutlak minimum değeri koymaktır, genellikle bu tek bir satırdır. İlk örnek, bir döngünün parçası olarak daha iyi olabilir, örneğinfor key in ["foo", "bar", "baz"]: try: A[key] = B[key]
Zim

2

Diğer dillerdeki kural, istisnai durumlar için istisnaları, yani düzenli kullanımda oluşmayan hatalar için ayırmaktır. Bu kuralın Python için nasıl geçerli olduğunu bilmiyorum, çünkü StopIteration bu kurala göre var olmamalıdır.


Bence bu kestane, istisna işlemenin pahalı olduğu ve bu nedenle performans üzerinde önemli bir etkiye sahip olduğu dillerden geldi. Bunun arkasında hiçbir zaman gerçek bir gerekçe veya mantık görmedim.
John La Rooy

@JohnLaRooy Hayır, performans gerçekten sebep değil. İstisnalar, bazılarının kodun okunabilirliğini engellediğini düşündüğü bir tür yerel olmayan gitmedir. Ancak, istisnaların bu şekilde kullanılması Python'da deyimsel olarak kabul edildiğinden yukarıdakiler geçerli değildir.
Ian Goldby

koşullu dönüşler aynı zamanda "yerel olmayan gitmedir" ve birçok kişi kod bloğunun sonundaki nöbetçileri incelemek yerine bu stili tercih eder.
cowbert

2

Başlangıç Python 3.8ve tanıtılması atama ifadeleri (PEP 572) ( :=operatör), biz durum değerini yakalayabilir dictB.get('hello', None)bir değişkene valuedoğru değilse, her iki kontrol etmek için None(aynı dict.get('hello', None)getiri nedeni ilişkili değer veya None) ve daha sonra bünyesinde kullanmak kondisyon:

# dictB = {'hello': 5, 'world': 42}
# dictA = {}
if value := dictB.get('hello', None):
  dictA["hello"] = value
# dictA is now {'hello': 5}

2
Değer == 0 ise bu başarısız olur
Eric

Bununla dict.get(key, None)aynı olduğunu unutmayın dict.get(key). ( belgeler )
Grilse

1

Şahsen, ikinci yönteme (ancak kullanarak has_key) eğiliyorum :

if B.has_key("blah"):
  A["blah"] = B["blah"]

Bu şekilde, her atama işlemi yalnızca iki satırdır (dene / hariç 4 yerine) ve atılan istisnalar, gerçek hatalar veya kaçırdığınız şeyler olacaktır (yalnızca orada olmayan tuşlara erişmeye çalışmak yerine) .

Görünüşe göre (sorunuzla ilgili yorumlara bakın), has_keykullanımdan kaldırıldı - bu yüzden sanırım şu şekilde yazılması daha iyi

if "blah" in B:
  A["blah"] = B["blah"]

1

Kabul edilen cevabın "atlamadan önce bak" ilkesine vurgusu çoğu dil için geçerli olsa da, python ilkelerine dayanan ilk yaklaşım daha pitonik olabilir. Python'da yasal bir kodlama stili olduğundan bahsetmiyorum bile. Önemli olan, try hariç bloğunu doğru bağlamda kullandığınızdan ve en iyi uygulamaları takip ettiğinizden emin olmaktır. Örneğin. bir try bloğunda çok fazla şey yapmak, çok geniş bir istisnayı yakalamak veya daha kötüsü - cümle dışında çıplak vb.

Af dilemek izin istemekten daha kolaydır. (EAFP)

Python dokümanları referansına buradan bakın .

Ayrıca, çekirdek geliştiricilerden biri olan Brett'in bu blogu , bunun çoğuna kısaca değiniyor.

Burada başka bir SO tartışmasına bakın :

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.