खाली सूची वाले डिफ़ॉल्ट मापदंडों से बचने के लिए पायथोनिक तरीका क्या है?


117

कभी-कभी डिफ़ॉल्ट पैरामीटर होना स्वाभाविक है जो एक खाली सूची है। अभी तक अजगर इन स्थितियों में अप्रत्याशित व्यवहार देता है ।

यदि उदाहरण के लिए, मेरे पास एक फ़ंक्शन है:

def my_func(working_list = []):
    working_list.append("a")
    print(working_list)

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

तो, व्यवहार इच्छा (प्रत्येक कॉल पर एक ताजा सूची) प्राप्त करने का पाइथोनिक तरीका क्या है?


17
अगर किसी में दिलचस्पी है कि ऐसा क्यों होता है, तो effbot.org/zone/default-values.htm देखें
— रयान हेनिंग

समान व्यवहार सेट्स के लिए होता है, हालाँकि आपको बग के रूप में दिखाने के लिए इसके लिए कुछ अधिक जटिल उदाहरण की आवश्यकता होती है।
— abeboparebop

जैसा कि लिंक मरते हैं, मुझे स्पष्ट रूप से इंगित करना चाहिए कि यह वांछित व्यवहार है। डिफ़ॉल्ट चर का मूल्यांकन फ़ंक्शन परिभाषा पर किया जाता है (जो पहली बार ऐसा होता है) कहा जाता है, और प्रत्येक बार फ़ंक्शन को नहीं कहा जाता है। नतीजतन, यदि आप एक उत्परिवर्ती डिफ़ॉल्ट तर्क को म्यूट करते हैं, तो बाद के किसी भी फ़ंक्शन कॉल केवल उत्परिवर्तित ऑब्जेक्ट का उपयोग कर सकते हैं।
— मोरिट्ज़

जवाबों:


150
def my_func(working_list=None):
    if working_list is None: 
        working_list = []

    working_list.append("a")
    print(working_list)

डॉक्स का कहना है कि आपको फ़ंक्शन के शरीर में इसके लिएNone डिफ़ॉल्ट और स्पष्ट रूप से परीक्षण करना चाहिए ।


1
क्या यह कहना बेहतर है: अगर working_list == कोई नहीं: या अगर working_list: ??
— जॉन मुल्डर

2
यह अजगर में करने के लिए पसंदीदा तरीका है, भले ही मुझे यह पसंद नहीं है क्योंकि यह बदसूरत है। मैं कहता हूं कि सबसे अच्छा अभ्यास "यदि वर्किंग_लिस्ट कोई नहीं है" होगा।
— ई-सतीस

21
इस उदाहरण में पसंदीदा तरीका यह है कि: यदि वर्किंग_लिस्ट कोई नहीं है। हो सकता है कि कॉलर ने कस्टम ऐपेंड के साथ खाली सूची जैसी वस्तु का उपयोग किया हो।
— tzot

5
मोहित रांका: सावधान रहें कि वर्किंग_लिस्ट सही नहीं है अगर इसकी लंबाई 0. है। यह असंगत व्यवहार की ओर जाता है: यदि फ़ंक्शन इसमें कुछ तत्व के साथ एक सूची प्राप्त करता है, तो इसके कॉलर के पास उसकी सूची अपडेट होगी, और यदि सूची खाली है, तो यह छुआ नहीं जाएगा।
— विन्सेन्ट

1
@PatrickT सही उपकरण मामले पर निर्भर करता है - एक varargs फ़ंक्शन एक (वैकल्पिक) सूची तर्क लेने वाले से बहुत अलग है। आपके द्वारा चुने जाने वाले स्थान जहाँ आप सोचते हैं कि कम बार आते हैं। जब आर्ग काउंट में परिवर्तन होता है, तो वैरग बहुत अच्छा होता है, लेकिन जब कोड WRITTEN होता है, तो यह तय होता है। अपने उदाहरण की तरह। यदि यह रनटाइम-चर है, या आप f()किसी सूची में कॉल करना चाहते हैं, तो आपको कॉल करना होगा f(*l)जो कि सकल है। इससे भी बदतर, कार्यान्वयन mate(['larch', 'finch', 'robin'], ['bumble', 'honey', 'queen'])SUCK w / varargs होगा। बहुत बेहतर है अगर यह def mate(birds=[], bees=[]):।
— FeRD

26

मौजूदा उत्तरों ने पहले ही सीधे समाधान प्रदान कर दिए हैं। हालांकि, चूंकि यह नए पायथन प्रोग्रामर्स के लिए एक बहुत ही सामान्य नुकसान है, इसलिए यह इस स्पष्टीकरण को जोड़ने के लायक है कि अजगर इस तरह से क्यों व्यवहार करता है, जो कि संक्षेप में " हिचकीकर्स गाइड टू पाइथन " में " म्यूटेबल डिफॉल्ट आर्ग्युमेंट्स " के रूप : http: // डॉक्स .python-guide.org / en / नवीनतम / लेखन / gotchas /

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

इसे लागू करने के लिए नमूना कोड:

def foo(element, to=None):
    if to is None:
        to = []
    to.append(element)
    return to

13

ऐसा नहीं है कि यह इस मामले में मायने रखता है, लेकिन आप किसी के लिए परीक्षण करने के लिए वस्तु पहचान का उपयोग कर सकते हैं:

if working_list is None: working_list = []

आप यह भी लाभ उठा सकते हैं कि बूलियन ऑपरेटर या अजगर में कैसे परिभाषित किया जाता है:

working_list = working_list or []

यद्यपि यह अप्रत्याशित रूप से व्यवहार करेगा यदि कॉलर आपको एक खाली सूची देता है (जो कि काम के लिए झूठे के रूप में गिना जाता है) और आपके द्वारा दी गई सूची को संशोधित करने के लिए आपके फ़ंक्शन की अपेक्षा करता है।


10

यदि फ़ंक्शन का इरादा पास किए गए पैरामीटर को संशोधित करना हैworking_list , तो हेनरीआर का उत्तर (= कोई नहीं, अंदर कोई भी नहीं देखें) देखें।

लेकिन अगर आप तर्क को म्यूट करने का इरादा नहीं रखते थे, तो बस इसे एक सूची के लिए शुरुआती बिंदु के रूप में उपयोग करें, आप बस इसे कॉपी कर सकते हैं:

def myFunc(starting_list = []):
    starting_list = list(starting_list)
    starting_list.append("a")
    print starting_list

(या इस साधारण मामले में, print starting_list + ["a"]लेकिन मुझे लगता है कि यह सिर्फ एक खिलौना उदाहरण था)

सामान्य तौर पर, अपने तर्कों को बदलना पायथन में बुरी शैली है। ऑब्जेक्ट को म्यूट करने के लिए पूरी तरह से अपेक्षित एकमात्र फ़ंक्शन ऑब्जेक्ट के तरीके हैं। यह एक वैकल्पिक तर्क को म्यूट करने के लिए भी दुर्लभ है - एक साइड इफेक्ट है जो केवल कुछ कॉल में होता है वास्तव में सबसे अच्छा इंटरफ़ेस?

  • यदि आप इसे "आउटपुट तर्क" की सी आदत से करते हैं, तो यह पूरी तरह से अनावश्यक है - आप हमेशा एक टपल के रूप में कई मान वापस कर सकते हैं।

  • यदि आप मध्यवर्ती सूचियों के निर्माण के बिना परिणामों की एक लंबी सूची को कुशलतापूर्वक बनाने के लिए ऐसा करते हैं, तो इसे एक जनरेटर के रूप में लिखने और result_list.extend(myFunc())जब आप इसे बुला रहे हों तो इसका उपयोग करने पर विचार करें । इस तरह आपके कॉलिंग कन्वेंशन बहुत साफ रहते हैं।

एक पैटर्न जहां एक वैकल्पिक arg को बार-बार किया जाता है, वह पुनरावर्ती कार्यों में एक छिपा हुआ "ज्ञापन" arg है:

def depth_first_walk_graph(graph, node, _visited=None):
    if _visited is None:
        _visited = set()  # create memo once in top-level call

    if node in _visited:
        return
    _visited.add(node)
    for neighbour in graph[node]:
        depth_first_walk_graph(graph, neighbour, _visited)

3

मैं ऑफ-टॉपिक हो सकता हूं, लेकिन याद रखिए कि यदि आप केवल एक चर संख्या में तर्क पारित करना चाहते हैं, तो पाइथोनिक तरीका एक टपल *argsया एक शब्दकोश पारित करना है **kargs। ये वैकल्पिक हैं और वाक्यविन्यास से बेहतर हैं myFunc([1, 2, 3])।

यदि आप एक टपल पास करना चाहते हैं:

def myFunc(arg1, *args):
  print args
  w = []
  w += args
  print w
>>>myFunc(1, 2, 3, 4, 5, 6, 7)
(2, 3, 4, 5, 6, 7)
[2, 3, 4, 5, 6, 7]

यदि आप एक शब्दकोश पारित करना चाहते हैं:

def myFunc(arg1, **kargs):
   print kargs
>>>myFunc(1, option1=2, option2=3)
{'option2' : 2, 'option1' : 3}

0

पहले से ही अच्छे और सही उत्तर दिए गए हैं। मैं बस एक और वाक्यविन्यास देना चाहता था कि आप क्या करना चाहते हैं, जो मुझे तब और सुंदर लगता है, जब आप उदाहरण के लिए डिफ़ॉल्ट सूची के साथ एक वर्ग बनाना चाहते हैं:

class Node(object):
    def __init__(self, _id, val, parents=None, children=None):
        self.id = _id
        self.val = val
        self.parents = parents if parents is not None else []
        self.children = children if children is not None else []

यह स्निपेट यदि अन्य ऑपरेटर सिंटैक्स का उपयोग करता है। मुझे यह विशेष रूप से पसंद है क्योंकि यह कॉलोनों के बिना एक साफ सा एक-लाइनर है, आदि शामिल हैं और यह लगभग एक सामान्य अंग्रेजी वाक्य की तरह पढ़ता है। :)

आपके मामले में आप लिख सकते हैं

def myFunc(working_list=None):
    working_list = [] if working_list is None else working_list
    working_list.append("a")
    print working_list

-3

मैंने यूसीएससी विस्तार वर्ग लिया Python for programmer

जो सच है: डीएफ एफएन (डेटा = []):

a) एक अच्छा विचार है ताकि आपकी डेटा सूचियाँ हर कॉल के साथ खाली होने लगें।

बी) एक अच्छा विचार है ताकि फ़ंक्शन को सभी कॉल जो कॉल पर कोई तर्क नहीं देते हैं उन्हें डेटा के रूप में खाली सूची मिल जाएगी।

c) एक उचित विचार है जब तक आपका डेटा तार की एक सूची है।

डी) एक बुरा विचार है क्योंकि डिफ़ॉल्ट [] डेटा जमा करेगा और डिफ़ॉल्ट [] बाद की कॉल के साथ बदल जाएगा।

उत्तर:

डी) एक बुरा विचार है क्योंकि डिफ़ॉल्ट [] डेटा जमा करेगा और डिफ़ॉल्ट [] बाद की कॉल के साथ बदल जाएगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.