शब्दकोश और डिफ़ॉल्ट मान


213

मान connectionDetailsलेना एक पायथन डिक्शनरी है, इस तरह के रिफैक्टिंग कोड का सबसे अच्छा, सबसे सुरुचिपूर्ण, सबसे "पायथोनिक" तरीका क्या है?

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

जवाबों:


311

ऐशे ही:

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

40
ध्यान दें कि दूसरा तर्क एक मूल्य है, कुंजी नहीं।
मार्सिन

7
+1 पठनीयता के लिए, लेकिन if/elseबहुत तेज़ है। वह भूमिका निभा सकता है या नहीं।
टिम पीटरज़

7
@ टिम, क्या आप एक संदर्भ प्रदान कर सकते हैं कि क्यों if/elseतेज है?
निशांतजोर

2
@ समय: मैंने यह मान लिया था कि उच्च स्तर की भाषा का उपयोग करने के फायदों में से एक यह है कि दुभाषिया फंक्शन्स के अंदर 'देख' सकेगा और उसे ऑप्टिमाइज़ कर सकेगा - जिससे उपयोगकर्ता को माइक्रो-ऑप्टिमाइज़ेशन से निपटना नहीं पड़ेगा। । क्या ऐसा नहीं है कि जेआईटी संकलन जैसी चीजें किसके लिए हैं?
निशांतजोर

3
@ निश्चिंत: पायथन (कम से कम सीपीथॉन, सबसे आम संस्करण) में जेआईटी संकलन नहीं है। PyPy वास्तव में इसे तेजी से हल कर सकता है, लेकिन मुझे वह स्थापित नहीं मिला है क्योंकि मानक पायथन हमेशा मेरे उद्देश्यों के लिए अब तक काफी तेज रहा है। सामान्य तौर पर, यह वास्तविक जीवन में मायने नहीं रखता है - यदि आपको समय-क्रिटिकल संख्या
क्रंच

99

आप भी इस defaultdictतरह का उपयोग कर सकते हैं :

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

आप लंबोदर के बजाय कोई भी सामान्य कार्य कर सकते हैं:

from collections import defaultdict
def a():
  return 4

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

7
मैं ओपी के प्रश्न की तुलना में कुछ अलग समस्या के लिए यहां आया था, और आपका समाधान इसे हल करता है।
0xc0de

मैं इसे +1 करूंगा लेकिन दुख की बात है कि यह getया इसी तरह के तरीकों में फिट नहीं है ।
0xc0de

डिक्शनरी में डिफ़ॉल्ट कुंजियों में परिवर्धन सुनिश्चित करने के लिए यह उत्तर मेरे लिए उपयोगी था। StackOverflow के उत्तर में वर्णन करने के लिए मेरा कार्यान्वयन थोड़ा लंबा है, इसलिए मैंने इसके बारे में यहाँ लिखा है। persagen.com/2020/03/05/…
विक्टोरिया स्टुअर्ट

24

हालांकि .get()यह एक अच्छा मुहावरा है, (यह धीमी है if/elseऔर try/exceptअगर शब्दकोष में कुंजी की मौजूदगी से सबसे अधिक उम्मीद की जा सकती है) की तुलना में धीमी है :

>>> 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
मैं अभी भी नहीं देखता कि क्यों if/then तेज होगा। दोनों ही मामलों में एक देखने शब्दकोश की आवश्यकता होती है, और जब तक के आह्वान get()है तो बहुत धीमी, और क्या मंदी के लिए खातों?
जेन्स

1
@ जेन्स: फंक्शन कॉल महंगे हैं।
टिम पीटरज़

1
भारी आबादी वाले शब्दकोश में कौन सी बड़ी बात नहीं होनी चाहिए, सही? यदि वास्तविक लुकिंग महंगा है तो फंक्शन कॉल ज्यादा मायने नहीं रखता है। यह शायद केवल खिलौना उदाहरणों में मायने रखता है।
AturSams

2
@zehelvion: शब्दकोश लुकअप O(1)शब्दकोश आकार की परवाह किए बिना है, इसलिए फ़ंक्शन कॉल ओवरहेड प्रासंगिक है।
टिम पीटरज़

35
यह एक विचित्र बात है अगर किसी फ़ंक्शन को कॉल करने का ओवरहेड आपको उपयोग करने के विरुद्ध निर्णय देगा। अपने साथी टीम के सदस्य सबसे अच्छा पढ़ सकते हैं।
जोहान बेडरसोदरफर

19

कई अलग-अलग चूक के लिए यह प्रयास करें:

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
यह एक अच्छा मुहावरेदार समाधान है, लेकिन एक नुकसान है। अनपेक्षित परिणाम हो सकते हैं यदि कनेक्शन None-वैल्यू को कुंजी-मूल्य वाले जोड़े में मूल्यों में से एक के रूप में या रिक्त स्ट्रिंग के साथ आपूर्ति की जाती है । defaultsशब्दकोश संभवतः अपने मूल्यों में से एक अनजाने बाहर blanked हो सकता था। (यह भी देखें stackoverflow.com/questions/6354436 )
dreftymac

9

ऐसा करने के लिए अजगर शब्दकोशों में एक विधि है: dict.setdefault

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

हालांकि इस पद्धति का मूल्य सेट connectionDetails['host']करने के लिए someDefaultValueकरता है, तो कुंजी hostपहले से ही परिभाषित नहीं है, क्या प्रश्न पूछा विपरीत है।


1
ध्यान दें कि setdefault()मान लौटाता है, इसलिए यह भी काम करता है host = connectionDetails.setdefault('host', someDefaultValue):। बस सावधान रहें कि connectionDetails['host']यदि कुंजी पहले नहीं थी, तो यह डिफ़ॉल्ट मान पर सेट हो जाएगा ।
ash108

7

(यह एक देर से जवाब है)

एक विकल्प dictवर्ग को उप- वर्ग करना और __missing__()विधि को लागू करना है, जैसे:

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

उदाहरण:

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

पायथन 3.3.5 के लिए PyPy (5.2.0-Alpha0) की स्थिति के बारे में @Tim Pietzcker के संदेह का परीक्षण करना, मुझे लगता है कि वास्तव में दोनों .get()और if/ elseतरह समान प्रदर्शन करते हैं। वास्तव में ऐसा लगता है कि अगर / और मामले में केवल एक ही लुकअप है, तो स्थिति और असाइनमेंट में एक ही कुंजी शामिल है (पिछले मामले की तुलना में जहां दो लुकअप हैं)।

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

आप वन-लाइनर के रूप में इसके लिए लांबा फ़ंक्शन का उपयोग कर सकते हैं। एक नई वस्तु बनाएं connectionDetails2जिसे एक फ़ंक्शन की तरह एक्सेस किया जाता है ...

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

अब उपयोग करें

connectionDetails2(k)

के बजाय

connectionDetails[k]

यदि kकुंजी में शब्दकोश मूल्य वापस आता है, अन्यथा यह वापस आ जाता है"DEFAULT"


मैं तुम्हें upvoted लेकिन अपने समाधान के साथ समस्या के साथ कि dicts काम [] लेकिन साथ () lambdas काम है
huksay yukashima
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.