Sözlükler ve varsayılan değerler


213

connectionDetailsBir Python sözlüğü olduğunu varsayarsak , bunun gibi kodları yeniden düzenlemenin en iyi, en zarif, en "pythonic" yolu nedir?

if "host" in connectionDetails:
    host = connectionDetails["host"]
else:
    host = someDefaultValue

Yanıtlar:


311

Bunun gibi:

host = connectionDetails.get('host', someDefaultValue)

40
İkinci argümanın bir anahtar değil, bir değer olduğunu unutmayın.
Marcin

7
Okunabilirlik için +1, ancak if/elseçok daha hızlı. Bu bir rol oynayabilir veya oynamaz.
Tim Pietzcker

7
@ Zaman, neden if/elsedaha hızlı olduğu konusunda bir referans verebilir misiniz?
nishantjr

2
@Tim: Daha yüksek bir dil kullanmanın avantajlarından birinin, yorumlayıcının işlevlerin içinde 'görebileceğini' ve optimize edebileceğini - kullanıcının mikro optimizasyonlarla o kadar uğraşmak zorunda olmayacağını varsaymıştım. . JIT derlemesi gibi şeyler bunun için değil mi?
nishantjr

3
@nishantjr: Python (en azından CPython, en yaygın varyant) JIT derlemesine sahip değil. PyPy gerçekten bunu daha hızlı çözebilir, ancak standart Python şimdiye kadar amacım için yeterince hızlı olduğu için bunu yüklemedim. Genel olarak, gerçek hayatta önemli değildir - zaman kritik sayı çatırtısı yapmanız gerekiyorsa, Python muhtemelen tercih edilen dil değildir ...
Tim Pietzcker

99

Bunun gibi de kullanabilirsiniz defaultdict:

from collections import defaultdict
a = defaultdict(lambda: "default", key="some_value")
a["blabla"] => "default"
a["key"] => "some_value"

Lambda yerine herhangi bir sıradan işlevi geçebilirsiniz:

from collections import defaultdict
def a():
  return 4

b = defaultdict(a, key="some_value")
b['absent'] => 4
b['key'] => "some_value"

7
Buraya OP'nin sorusundan farklı bir sorun için geldim ve çözümünüz tam olarak çözüyor.
0xc0de

+ 1'leyecektim ama ne yazık ki getbenzer yöntemlere uymuyor .
0xc0de

Bu yanıt, varsayılan anahtarlar içeren bir sözlüğe eklemeleri sağlamak için benim için yararlı oldu. Uygulamam bir StackOverflow cevabında tanımlamak için biraz fazla uzun, bu yüzden burada yazdım. persagen.com/2020/03/05/…
Victoria Stuart

24

.get()Güzel bir deyim olsa da, daha yavaş if/else(ve try/exceptsözlükte anahtarın bulunması beklenenden daha yavaştır ):

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

3
Hala neden if/then daha hızlı olacağını anlamıyorum . Her iki durum da bir sözlük aramasını gerektirir ve çağırma sürece get()olduğunu bu yüzden çok daha yavaş, yavaşlama başka ne hesaplar?
Jens

1
@Jens: İşlev çağrıları pahalıdır.
Tim Pietzcker

1
Ağır nüfuslu bir sözlükte hangisi büyük bir sorun olmamalı, doğru mu? Yani, gerçek arama maliyetli ise işlev çağrısının önemi olmayacaktır. Muhtemelen sadece oyuncak örneklerinde önemlidir.
AturSams

2
@zehelvion: Sözlük araması O(1)sözlük boyutundan bağımsızdır, dolayısıyla fonksiyon çağrısı yükü önemlidir.
Tim Pietzcker

35
bir işlevi çağırmanın ek yükü get kullanmaya karşı karar vermenizi sağlarsa tuhaftır. Ekip üyelerinizin en iyi okuyabileceklerini kullanın.
Jochen Bedersdorfer

19

Birden çok farklı varsayılan için şunu deneyin:

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

3
Bu iyi bir deyimsel çözüm, ancak bir tuzak var. ConnectionDetails ile Noneanahtar / değer çiftlerindeki değerlerden biri olarak emptyString sağlanırsa veya emptyString beklenmedik sonuçlar doğurabilir . defaultsSözlük potansiyel onun değerlerinden biri istemeden boş bırakıldı olabilir. (ayrıca bkz. stackoverflow.com/questions/6354436 )
dreftymac

9

Python sözlüklerinde bunu yapmak için bir yöntem vardır: dict.setdefault

connectionDetails.setdefault('host',someDefaultValue)
host = connectionDetails['host']

Ancak bu yöntem değerini ayarlar connectionDetails['host']için someDefaultValueanahtar eğer hostzaten soru sorduk aksine tanımlı değil.


1
Not olduğunu setdefault()döndürür değeri, bu da iyi çalışır, böylece: host = connectionDetails.setdefault('host', someDefaultValue). connectionDetails['host']Anahtar daha önce orada değilse varsayılan değere ayarlanacağına dikkat edin .
ash108

7

(bu geç bir cevaptır)

Bir alternatif, sınıfın alt dictsınıfını oluşturmak ve __missing__()yöntemi şu şekilde uygulamaktır :

class ConnectionDetails(dict):
    def __missing__(self, key):
        if key == 'host':
            return "localhost"
        raise KeyError(key)

Örnekler:

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

4

@Tim Pietzcker'ın Python 3.3.5 için PyPy'deki (5.2.0-alpha0) durum hakkındaki şüphesini test ederek, hem hem .get()de if/ elseyolunun benzer performans gösterdiğini görüyorum . Aslında if / else durumunda, koşul ve atama aynı anahtarı içeriyorsa yalnızca tek bir arama bile var (iki aramanın olduğu son durumla karşılaştırın).

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

1

Bunun için bir lamba fonksiyonunu tek astar olarak kullanabilirsiniz. connectionDetails2İşlev gibi erişilen yeni bir nesne oluşturun ...

connectionDetails2 = lambda k: connectionDetails[k] if k in connectionDetails.keys() else "DEFAULT"

Şimdi kullanın

connectionDetails2(k)

onun yerine

connectionDetails[k]

eğer kanahtarlar içindeyse sözlük değerini döndürür, aksi takdirde döndürür"DEFAULT"


Sana upvoted ama çözüm ile sorun) (ile bununla dicts çalışma [] ancak lambda'lar işidir
huksay yukashima
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.