'सेटडेफ़ॉल्ट' के लिए मामलों का प्रयोग करें


192

के अलावा collections.defaultdictअजगर 2.5 में बहुत लिए की जरूरत कम dictकी setdefaultविधि। यह सवाल हमारी सामूहिक शिक्षा के लिए है:

  1. setdefaultपाइथन 2.6 / 2.7 में आज भी क्या उपयोगी है?
  2. किस लोकप्रिय उपयोग के मामलों के setdefaultसाथ अलग किया गया था collections.defaultdict?

1
थोड़ा संबंधित भी stackoverflow.com/questions/7423428/…
उपयोगकर्ता

जवाबों:


208

आप कह सकते हैं defaultdictसेटिंग्स चूक के लिए उपयोगी है dict भरने से पहले और setdefaultडिफ़ॉल्ट सेट के लिए उपयोगी है , जबकि या dict भरने के बाद

संभवत: सबसे आम उपयोग मामला: आइटम समूहीकरण (बिना डेटा के, अन्य उपयोग itertools.groupby)

# really verbose
new = {}
for (key, value) in data:
    if key in new:
        new[key].append( value )
    else:
        new[key] = [value]


# easy with setdefault
new = {}
for (key, value) in data:
    group = new.setdefault(key, []) # key might exist already
    group.append( value )


# even simpler with defaultdict 
from collections import defaultdict
new = defaultdict(list)
for (key, value) in data:
    new[key].append( value ) # all keys have a default already

कभी-कभी आप यह सुनिश्चित करना चाहते हैं कि तानाशाही बनाने के बाद विशिष्ट कुंजी मौजूद हो। defaultdictइस मामले में काम नहीं करता है, क्योंकि यह केवल स्पष्ट पहुंच पर चाबियाँ बनाता है। सोचें कि आप कई हेडर के साथ HTTP-ish का उपयोग करते हैं - कुछ वैकल्पिक हैं, लेकिन आप उनके लिए चूक चाहते हैं:

headers = parse_headers( msg ) # parse the message, get a dict
# now add all the optional headers
for headername, defaultvalue in optional_headers:
    headers.setdefault( headername, defaultvalue )

1
दरअसल, यह IMHO प्रतिस्थापन के लिए मुख्य उपयोग का मामला है defaultdict। क्या आप इसका उदाहरण दे सकते हैं कि पहले पैराग्राफ में आपका क्या मतलब है?
एली बेंडरस्की

2
मुहम्मद अलकरौरी: जो आप पहले करते हैं वह हुकुम की नकल करता है फिर कुछ वस्तुओं को अधिलेखित करता है। मैं बहुत कुछ करता हूं और मुझे लगता है कि वास्तव में मुहावरे सबसे ज्यादा पसंद किए जाते हैं setdefaultdefaultdictयदि सभी defaultvaluesसमान नहीं हैं (तो कुछ हैं 0और कुछ हैं []) दूसरी ओर ए काम नहीं करेगा ।
जोहान रिट्जेल

2
@ YHC4k, हाँ। इसीलिए मैंने इस्तेमाल किया headers = dict(optional_headers)। मामले के लिए जब डिफ़ॉल्ट मान सभी समान नहीं हैं। और अंतिम परिणाम वही है जैसे अगर आपको पहले HTTP हेडर मिलते हैं तो उन डिफॉल्ट्स को सेट करें जो आपको नहीं मिले। और यह काफी उपयोगी है यदि आपके पास पहले से ही है optional_headers। मेरे दिए गए 2 स्टेप कोड को आज़माएं और उसकी तुलना करें, और आप देखेंगे कि मेरा क्या मतलब है।
मुहम्मद अलकरौरी

19
या बस करते हैंnew.setdefault(key, []).append(value)
13:25 पर felina

2
मुझे यह अजीब लगता है कि सबसे अच्छा जवाब उबलता है, defaultdictइससे भी बेहतर है setdefault(इसलिए जहां उपयोग मामला है?)। इसके अलावा, ChainMapबेहतर होगा httpउदाहरण, IMO।
यवस्गेरी

29

मैं आमतौर पर setdefaultखोजशब्द तर्क के लिए उपयोग करता हूं , जैसे कि इस फ़ंक्शन में:

def notify(self, level, *pargs, **kwargs):
    kwargs.setdefault("persist", level >= DANGER)
    self.__defcon.set(level, **kwargs)
    try:
        kwargs.setdefault("name", self.client.player_entity().name)
    except pytibia.PlayerEntityNotFound:
        pass
    return _notify(level, *pargs, **kwargs)

कीवर्ड तर्क लेने वाले फ़ंक्शंस के आसपास के रैपरों में तर्कों को जोड़ना बहुत अच्छा है।


16

defaultdict डिफ़ॉल्ट तब स्थिर होता है, जब नई सूची की तरह डिफ़ॉल्ट मान स्थिर होता है, लेकिन इतना अधिक नहीं कि यह गतिशील हो।

उदाहरण के लिए, मुझे स्ट्रेट को अनूठे किलों में मैप करने के लिए एक शब्दकोश की आवश्यकता है। defaultdict(int)हमेशा डिफ़ॉल्ट मान के लिए 0 का उपयोग करेगा। इसी तरह, defaultdict(intGen())हमेशा 1 पैदा करता है।

इसके बजाय, मैंने एक नियमित रूप से तानाशाही का इस्तेमाल किया:

nextID = intGen()
myDict = {}
for lots of complicated stuff:
    #stuff that generates unpredictable, possibly already seen str
    strID = myDict.setdefault(myStr, nextID())

ध्यान दें कि dict.get(key, nextID())अपर्याप्त है क्योंकि मुझे इन मूल्यों को बाद में भी संदर्भित करने में सक्षम होने की आवश्यकता है।

intGen एक छोटा वर्ग है जो मैं निर्माण करता हूं जो स्वचालित रूप से एक इंट्री बढ़ाता है और उसका मूल्य लौटाता है:

class intGen:
    def __init__(self):
        self.i = 0

    def __call__(self):
        self.i += 1
    return self.i

अगर किसी के पास ऐसा करने का तरीका है तो defaultdictमैं इसे देखना पसंद करूंगा।


डिफ़ॉल्ट के साथ इसे करने के तरीके के लिए (डिफ़ॉल्ट का एक उपवर्ग), इस सवाल को देखें: stackoverflow.com/questions/2912231/…
वेरोनिका

8
आप के intGenसाथ बदल सकता है itertools.count().next
एंटीमनी

7
nextID()प्रत्येक मूल्य में वृद्धि होने की myDict.setdefault()बात कही जा रही है, भले ही वह मूल्य जो रिटर्न के रूप में उपयोग नहीं किया जाता है strID। यह किसी भी तरह से बेकार लगता है और उन चीजों में से एक को दिखाता है जो मुझे setdefault()सामान्य रूप से पसंद नहीं है - अर्थात यह हमेशा अपने defaultतर्क का मूल्यांकन करता है कि क्या यह वास्तव में उपयोग किया जाता है या नहीं।
19

आप के साथ ऐसा कर सकते हैं defaultdict: myDict = defaultdict(lambda: nextID())। बाद strID = myDict[myStr]में, लूप में।
मुशीफिल

3
डिफ़ॉल्ट व्यवहार के साथ आपके द्वारा वर्णित व्यवहार प्राप्त करने के लिए, बस क्यों नहीं myDict = defaultdict(nextID)?
चालीस_ट्वो १५'१

10

मैं का उपयोग करें setdefault()जब मैं एक में एक डिफ़ॉल्ट मान चाहते हैं OrderedDict। एक मानक पायथन संग्रह नहीं है जो दोनों करता है, लेकिन इस तरह के संग्रह को लागू करने के तरीके हैं


9

जब कोई कुंजी मौजूद नहीं होती है, तो अधिकांश उत्तर राज्य setdefaultया defaultdictआपको एक डिफ़ॉल्ट मान सेट करने देता है। हालाँकि, मैं उपयोग के मामलों के संबंध में एक छोटी सी चेतावनी बताना चाहूंगा setdefault। जब पायथन दुभाषिया निष्पादित होता है, setdefaultतो यह हमेशा फ़ंक्शन के दूसरे तर्क का मूल्यांकन करेगा, भले ही कुंजी शब्दकोश में मौजूद हो। उदाहरण के लिए:

In: d = {1:5, 2:6}

In: d
Out: {1: 5, 2: 6}

In: d.setdefault(2, 0)
Out: 6

In: d.setdefault(2, print('test'))
test
Out: 6

जैसा कि आप देख सकते हैं, printयह भी निष्पादित किया गया था , हालांकि 2 पहले से ही शब्दकोश में मौजूद था। यह विशेष रूप से महत्वपूर्ण हो जाता है यदि आप setdefaultएक अनुकूलन जैसे उदाहरण के लिए उपयोग करने की योजना बना रहे हैं memoization। यदि आप दूसरे तर्क के रूप में एक पुनरावर्ती फ़ंक्शन कॉल को जोड़ते हैं setdefault, तो आपको इससे कोई भी प्रदर्शन नहीं मिलेगा क्योंकि पायथन हमेशा फ़ंक्शन को पुनरावर्ती कहेगा।

चूंकि मेमोइज़ेशन का उल्लेख किया गया था, यदि आप फ़ंक्शन को मेमोलाइज़ेशन के साथ बढ़ाने पर विचार करते हैं, तो एक बेहतर विकल्प फंमरोसिल्स_क्रूज़ डेकोरेटर का उपयोग करना है। lru_cache एक पुनरावर्ती फ़ंक्शन के लिए कैशिंग आवश्यकताओं को बेहतर तरीके से संभालता है।


8

जैसा कि मुहम्मद ने कहा, ऐसी स्थितियां हैं जिनमें आप केवल कभी-कभी एक डिफ़ॉल्ट मान सेट करना चाहते हैं। इसका एक बड़ा उदाहरण एक डेटा संरचना है जो पहले आबादी है, फिर बोली जाती है।

एक तिकड़ी पर विचार करें। एक शब्द जोड़ते समय, यदि एक सबनोड की आवश्यकता होती है, लेकिन मौजूद नहीं है, तो इसे त्रि का विस्तार करने के लिए बनाया जाना चाहिए। जब किसी शब्द की उपस्थिति के लिए क्वेरी करते हैं, तो एक लापता सबनॉड इंगित करता है कि शब्द मौजूद नहीं है और इसे बनाया नहीं जाना चाहिए।

एक डिफ़ॉल्ट निर्णय ऐसा नहीं कर सकता। इसके बजाय, गेट और सेटडफॉल्ट विधियों के साथ एक नियमित रूप से तय किया जाना चाहिए।


5

सैद्धांतिक रूप से बोलना, setdefaultअभी भी आसान होगा यदि आप कभी-कभी डिफ़ॉल्ट सेट करना चाहते हैं और कभी-कभी नहीं। वास्तविक जीवन में, मैं इस तरह के उपयोग के मामले में नहीं आया हूं।

हालांकि, एक दिलचस्प उपयोग का मामला मानक पुस्तकालय (पायथन 2.6, _threadlocal.py) से आता है।

>>> mydata = local()
>>> mydata.__dict__
{'number': 42}
>>> mydata.__dict__.setdefault('widgets', [])
[]
>>> mydata.widgets
[]

मैं कहूंगा कि उपयोग __dict__.setdefaultकरना एक बहुत ही उपयोगी मामला है।

संपादित करें : जैसा कि होता है, मानक पुस्तकालय में यह एकमात्र उदाहरण है और यह एक टिप्पणी में है। तो क्या यह अस्तित्व के औचित्य के लिए एक मामले के लिए पर्याप्त नहीं है setdefault। फिर भी, यहाँ एक स्पष्टीकरण है:

ऑब्जेक्ट अपनी विशेषताओं को विशेषता में संग्रहीत करते हैं __dict__। जैसा कि होता है, __dict__विशेषता किसी भी समय वस्तु निर्माण के बाद लेखन योग्य होती है। यह भी एक शब्दकोश नहीं है defaultdict। यह सामान्य मामले में वस्तुओं के लिए समझदार नहीं __dict__है, defaultdictक्योंकि प्रत्येक वस्तु को सभी कानूनी पहचानकर्ताओं के पास गुण के रूप में होना चाहिए। इसलिए मैं पायथन ऑब्जेक्ट्स से छुटकारा पाने के लिए किसी भी बदलाव की उम्मीद नहीं कर सकता __dict__.setdefault, इसके अलावा इसे पूरी तरह से हटाने के अलावा अगर इसे उपयोगी नहीं माना जाता।


1
क्या आप विस्तार से बता सकते हैं - क्या _dict .setdefault को विशेष रूप से उपयोगी बनाता है?
एली बेंडरस्की

1
@ एली: मुझे लगता है कि बिंदु यह है कि __dict__dict, कार्यान्वयन से है , न कि ए defaultdict
बजे कटिरेल

1
ठीक है। मैं setdefaultपायथन में रहने के बारे में बुरा नहीं मानता, लेकिन यह देखने के लिए उत्सुक है कि यह अब लगभग बेकार है।
एली बेंडरस्की

@ एली: मैं सहमत हूं। मुझे नहीं लगता कि आज इसे पेश किए जाने के लिए पर्याप्त कारण हैं। लेकिन पहले से ही होने के नाते, इसे हटाने के लिए बहस करना मुश्किल होगा, पहले से ही इसका उपयोग करने वाले सभी कोड को देखते हुए।
मुहम्मद अलकरौरी

1
रक्षात्मक प्रोग्रामिंग के तहत फाइल। setdefaultयह स्पष्ट करता है कि आप एक कुंजी के माध्यम से एक तानाशाही को सौंप रहे हैं जो मौजूद हो सकती है या नहीं भी हो सकती है, और यदि यह मौजूद नहीं है तो आप इसे डिफ़ॉल्ट मान के साथ बनाया जाना चाहते हैं: उदाहरण के लिए d.setdefault(key,[]).append(value)। कार्यक्रम में कहीं और आप ऐसा करते हैं alist=d[k]जहाँ k की गणना की जाती है, और आप चाहते हैं कि यदि कोई अपवाद हो तो k को d में नहीं रखा जाएगा (जो कि किसी डिफ़ॉल्ट के साथ आवश्यक हो सकता है assert k in dया यहाँ तक किif not ( k in d): raise KeyError
nigel222

3

defaultdictओवर dict( dict.setdefault) का एक दोष यह है कि एक defaultdictवस्तु एक नया आइटम बनाती है हर समय गैर मौजूदा कुंजी दी जाती है (जैसे कि ==, के साथ print)। इसके अलावा defaultdictकक्षा आम तौर पर कम आम है तोdict कक्षा, इसका IME क्रमबद्ध करना अधिक कठिन है।

PS IMO फ़ंक्शंस | किसी ऑब्जेक्ट को म्यूट करने के लिए विधियाँ नहीं, किसी ऑब्जेक्ट को म्यूट नहीं करना चाहिए।


यह हर बार एक नई वस्तु बनाने के लिए नहीं है। आप defaultdict(lambda l=[]: l)इसके बजाय आसानी से कर सकते हैं ।
अरेटियर

6
कभी भी ऐसा न करें जो @Artyer सुझाव देता है - परिवर्तनशील चूक आपको काटेगी।
ब्रैंडन हमर्ट

2

इसकी उपयोगिता दिखाने के लिए सेटडेफ़ॉल्ट के कुछ उदाहरण यहां दिए गए हैं:

"""
d = {}
# To add a key->value pair, do the following:
d.setdefault(key, []).append(value)

# To retrieve a list of the values for a key
list_of_values = d[key]

# To remove a key->value pair is still easy, if
# you don't mind leaving empty lists behind when
# the last value for a given key is removed:
d[key].remove(value)

# Despite the empty lists, it's still possible to 
# test for the existance of values easily:
if d.has_key(key) and d[key]:
    pass # d has some values for key

# Note: Each value can exist multiple times!
"""
e = {}
print e
e.setdefault('Cars', []).append('Toyota')
print e
e.setdefault('Motorcycles', []).append('Yamaha')
print e
e.setdefault('Airplanes', []).append('Boeing')
print e
e.setdefault('Cars', []).append('Honda')
print e
e.setdefault('Cars', []).append('BMW')
print e
e.setdefault('Cars', []).append('Toyota')
print e

# NOTE: now e['Cars'] == ['Toyota', 'Honda', 'BMW', 'Toyota']
e['Cars'].remove('Toyota')
print e
# NOTE: it's still true that ('Toyota' in e['Cars'])

2

मैं स्वीकार किए गए उत्तर को फिर से लिखता हूं और इसे न्यूबीज के लिए नया रूप देता हूं।

#break it down and understand it intuitively.
new = {}
for (key, value) in data:
    if key not in new:
        new[key] = [] # this is core of setdefault equals to new.setdefault(key, [])
        new[key].append(value)
    else:
        new[key].append(value)


# easy with setdefault
new = {}
for (key, value) in data:
    group = new.setdefault(key, []) # it is new[key] = []
    group.append(value)



# even simpler with defaultdict
new = defaultdict(list)
for (key, value) in data:
    new[key].append(value) # all keys have a default value of empty list []

इसके अतिरिक्त, मैंने संदर्भ के रूप में विधियों को वर्गीकृत किया:

dict_methods_11 = {
            'views':['keys', 'values', 'items'],
            'add':['update','setdefault'],
            'remove':['pop', 'popitem','clear'],
            'retrieve':['get',],
            'copy':['copy','fromkeys'],}

1

जब मैं इसे प्राप्त करता हूं, तो इसे अक्सर इस्तेमाल करता हूं, शब्दकोश में डिफ़ॉल्ट (!!!) की स्थापना करता हूं; कुछ हद तक os.environ शब्दकोश:

# Set the venv dir if it isn't already overridden:
os.environ.setdefault('VENV_DIR', '/my/default/path')

संक्षिप्त रूप से, यह इस तरह दिखता है:

# Set the venv dir if it isn't already overridden:
if 'VENV_DIR' not in os.environ:
    os.environ['VENV_DIR'] = '/my/default/path')

यह ध्यान देने योग्य है कि आप परिणामी चर का भी उपयोग कर सकते हैं:

venv_dir = os.environ.setdefault('VENV_DIR', '/my/default/path')

लेकिन डिफ़ॉल्ट रूप से मौजूद होने से पहले यह कम आवश्यक है।


1

एक अन्य उपयोग मामला जो मुझे नहीं लगता कि ऊपर उल्लेखित था। कभी-कभी आप अपनी आईडी द्वारा वस्तुओं का कैशे तानाशाही रखते हैं जहां प्राथमिक उदाहरण कैशे में होता है और आप लापता होने पर कैशे सेट करना चाहते हैं।

return self.objects_by_id.setdefault(obj.id, obj)

यह उपयोगी है जब आप हमेशा एक अलग आईडी प्रति एक उदाहरण रखना चाहते हैं, भले ही आप हर बार एक obj प्राप्त करें। उदाहरण के लिए जब ऑब्जेक्ट विशेषताओं को मेमोरी में अपडेट किया जाता है और स्टोरेज को सहेजना टाल दिया जाता है।


1

एक बहुत ही महत्वपूर्ण उपयोग-मामला जिसे मैंने अभी-अभी ठोकर खाया: dict.setdefault() जिसे बहु-थ्रेडेड कोड के लिए बहुत अच्छा है जब आप केवल एक ही विहित वस्तु चाहते हैं (जैसा कि कई वस्तुओं के विपरीत होता है जो बराबर होते हैं)।

उदाहरण के लिए, (Int)Flagपायथन 3.6.0 में Enum में एक बग है : यदि कई थ्रेड्स एक समग्र (Int)Flagसदस्य के लिए प्रतिस्पर्धा कर रहे हैं , तो एक से अधिक होने का अंत हो सकता है:

from enum import IntFlag, auto
import threading

class TestFlag(IntFlag):
    one = auto()
    two = auto()
    three = auto()
    four = auto()
    five = auto()
    six = auto()
    seven = auto()
    eight = auto()

    def __eq__(self, other):
        return self is other

    def __hash__(self):
        return hash(self.value)

seen = set()

class cycle_enum(threading.Thread):
    def run(self):
        for i in range(256):
            seen.add(TestFlag(i))

threads = []
for i in range(8):
    threads.append(cycle_enum())

for t in threads:
    t.start()

for t in threads:
    t.join()

len(seen)
# 272  (should be 256)

setdefault()गणना को कंपोजिट मेंबर मेंबर को सेव करने के आखिरी स्टेप के रूप में इस्तेमाल करना है - अगर दूसरे को पहले ही सेव किया जा चुका है तो यूनीक एनम मेंबर्स को गारंटी देते हुए नए के बजाय इसका इस्तेमाल किया जाता है।


0

[संपादित करें] बहुत गलत है!सेटफाल्ट हमेशा लंबे समय तक काम करता रहेगा, पायथन उत्सुक हो रहा है।

टटल के जवाब पर विस्तार। मेरे लिए सबसे अच्छा उपयोग मामला कैश तंत्र है। के बजाय:

if x not in memo:
   memo[x]=long_computation(x)
return memo[x]

जो 3 लाइनों और 2 या 3 लुक्स का उपभोग करता है, मैं खुशी से लिखूंगा :

return memo.setdefault(x, long_computation(x))

अच्छा उदाहरण। मुझे अभी भी लगता है कि 3 लाइनें अधिक समझ में आने वाली हैं, लेकिन शायद मेरा दिमाग सेटडेफॉल्ट की सराहना करने के लिए बढ़ेगा।
बॉब स्टीन

5
वे समकक्ष नहीं हैं। पहले में, long_computation(x)केवल तभी कहा जाता है यदि x not in memo। जबकि दूसरे में, long_computation(x)हमेशा कहा जाता है। केवल असाइनमेंट सशर्त है, setdefaultजैसा दिखने वाला समान कोड होगा: v = long_computation(x)/ if x not in memo:/ memo[x] = v
दान डी।

0

मुझे यहाँ दिया गया उत्तर पसंद है:

http://stupidpythonideas.blogspot.com/2013/08/defaultdict-vs-setdefault.html

संक्षेप में, निर्णय (गैर-प्रदर्शन-महत्वपूर्ण ऐप्स में) इस आधार पर किया जाना चाहिए कि आप खाली कुंजी के बहाव को कैसे संभालना चाहते हैं ( अर्थात KeyError बनाम डिफ़ॉल्ट मान)।


0

जब आप पहले से सेट कुंजी के मूल्य को अधिलेखित नहीं करना चाहते हैं तो इसके लिए अलग-अलग उपयोग का मामला setdefault()है । ओवरराइट करता है , जबकि ऐसा नहीं है। नेस्टेड शब्दकोशों के लिए यह अधिक बार होता है कि आप केवल एक डिफ़ॉल्ट सेट करना चाहते हैं यदि कुंजी अभी तक सेट नहीं है, क्योंकि आप वर्तमान उप शब्दकोश को निकालना नहीं चाहते हैं। यह तब होता है जब आप उपयोग करते हैंdefaultdictsetdefault()setdefault()

उदाहरण के साथ defaultdict:

>>> from collection import defaultdict()
>>> foo = defaultdict()
>>> foo['a'] = 4
>>> foo['a'] = 2
>>> print(foo)
defaultdict(None, {'a': 2})

setdefault ओवरराइट नहीं करता:

>>> bar = dict()
>>> bar.setdefault('a', 4)
>>> bar.setdefault('a', 2)
>>> print(bar)
{'a': 4}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.