पायथन में मापदंडों के नामकरण की बाध्यता


111

पायथन में आपके पास एक फ़ंक्शन परिभाषा हो सकती है:

def info(object, spacing=10, collapse=1)

जिसे निम्नलिखित तरीकों में से किसी में भी बुलाया जा सकता है:

info(odbchelper)                    
info(odbchelper, 12)                
info(odbchelper, collapse=0)        
info(spacing=15, object=odbchelper)

पायथन के किसी भी आदेश के तर्क की अनुमति देने के लिए धन्यवाद, इसलिए जब तक उनका नाम लिया जाता है।

समस्या यह है कि हमारे बड़े कार्यों में से कुछ बड़े होते हैं, लोग बीच में पैरामीटर जोड़ सकते हैं spacingऔर इसका collapseअर्थ है कि गलत मान उन मापदंडों पर जा सकते हैं जो नाम नहीं हैं। इसके अलावा कभी-कभी यह हमेशा स्पष्ट नहीं होता है कि अंदर जाने की क्या जरूरत है। हम लोगों को कुछ मापदंडों को नाम देने के लिए मजबूर करने के एक तरीके के बाद हैं - न केवल एक कोडिंग मानक, बल्कि आदर्श रूप से एक ध्वज या pydev प्लगइन?

इसलिए कि उपरोक्त 4 उदाहरणों में, केवल अंतिम ही चेक को पास करेगा क्योंकि सभी मापदंडों के नाम हैं।

ऑड्स हैं हम इसे केवल कुछ कार्यों के लिए चालू करेंगे, लेकिन इसे लागू करने के तरीके के बारे में कोई सुझाव - या यदि यह संभव है तो इसकी सराहना की जाएगी।

जवाबों:


214

पायथन 3 में - हां, आप *तर्क सूची में निर्दिष्ट कर सकते हैं ।

से डॉक्स :

"*" या "पहचानकर्ता" के बाद पैरामीटर केवल-कीवर्ड पैरामीटर हैं और केवल उपयोग किए गए कीवर्ड तर्क पास किए जा सकते हैं।

>>> def foo(pos, *, forcenamed):
...   print(pos, forcenamed)
... 
>>> foo(pos=10, forcenamed=20)
10 20
>>> foo(10, forcenamed=20)
10 20
>>> foo(10, 20)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() takes exactly 1 positional argument (2 given)

यह भी साथ जोड़ा जा सकता है **kwargs:

def foo(pos, *, forcenamed, **kwargs):

32

आप निम्न तरीके से एक फ़ंक्शन को परिभाषित करके लोगों को Python3 में कीवर्ड तर्कों का उपयोग करने के लिए मजबूर कर सकते हैं।

def foo(*, arg0="default0", arg1="default1", arg2="default2"):
    pass

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

def foo(**kwargs):
    pass

यह कॉल करने वाले को kwargs का उपयोग करने के लिए बाध्य करेगा, लेकिन यह उस समाधान का बहुत अच्छा नहीं है क्योंकि आपको केवल उस तर्क को स्वीकार करने के लिए एक चेक लगाना होगा जो आपको चाहिए।


11

यह सच है कि अधिकांश प्रोग्रामिंग लैंग्वेज फ़ंक्शन ऑर्डर को अनुबंध कॉल का हिस्सा बनाती हैं, लेकिन ऐसा करने की आवश्यकता नहीं है । क्यों होगा? प्रश्न के बारे में मेरी समझ यह है कि अगर पायथन इस संबंध में अन्य प्रोग्रामिंग भाषाओं से अलग है। पायथन 2 के लिए अन्य अच्छे उत्तरों के अलावा, कृपया निम्नलिखित पर विचार करें:

__named_only_start = object()

def info(param1,param2,param3,_p=__named_only_start,spacing=10,collapse=1):
    if _p is not __named_only_start:
        raise TypeError("info() takes at most 3 positional arguments")
    return str(param1+param2+param3) +"-"+ str(spacing) +"-"+ str(collapse)

एक कॉल करने वाला केवल तर्क spacingऔर collapseस्थिति के बिना (अपवाद के बिना) प्रदान करने में सक्षम होगा:

info(arg1, arg2, arg3, module.__named_only_start, 11, 2)

पहले से ही अन्य मॉड्यूल से संबंधित निजी तत्वों का उपयोग नहीं करने का सम्मेलन पाइथन में बहुत बुनियादी है। पायथन के साथ ही, मापदंडों के लिए यह सम्मेलन केवल अर्ध-लागू किया जाएगा।

अन्यथा, कॉल को फॉर्म का होना चाहिए:

info(arg1, arg2, arg3, spacing=11, collapse=2)

एक कॉल

info(arg1, arg2, arg3, 11, 2)

मान को 11 असाइन करेगा _pऔर फ़ंक्शन के पहले निर्देश द्वारा एक अपवाद बढ़ गया है।

विशेषताएँ:

  • पहले पैरामीटर _p=__named_only_start को स्थितिगत रूप से (या नाम से) स्वीकार किया जाता है।
  • बाद में पैरामीटर _p=__named_only_startकेवल नाम द्वारा प्रदान किया जाना चाहिए (जब तक कि विशेष प्रहरी वस्तु के बारे में ज्ञान न हो__named_only_start प्राप्त नहीं किया जाता है और उपयोग किया जाता है)।

पेशेवरों:

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

विपक्ष:

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

कृपया ध्यान रखें कि यह उत्तर केवल पायथन 2. 2 के लिए ही मान्य है। पायथन 3 इसी प्रकार का है, लेकिन अन्य उत्तरों में वर्णित बहुत ही सुंदर, भाषा समर्थित तंत्र है।

मैंने पाया है कि जब मैं अपना दिमाग खोलता हूं और इसके बारे में सोचता हूं, तो कोई सवाल या अन्य निर्णय मूर्खतापूर्ण, गूंगा या सिर्फ मूर्खतापूर्ण नहीं लगता है। इसके विपरीत काफी: मैं आमतौर पर बहुत कुछ सीखता हूं।


"जाँच रन-टाइम के दौरान होती है, संकलन-समय पर नहीं।" - मुझे लगता है कि यह सभी फ़ंक्शन तर्क जाँच का सच है। जब तक आप वास्तव में फ़ंक्शन इनवोकेशन की लाइन को निष्पादित नहीं करते हैं, तब तक आप हमेशा नहीं जानते हैं कि किस फ़ंक्शन को निष्पादित किया जा रहा है। इसके अलावा, +1 - यह चतुर है।
एरिक

@ एरिक: यह सिर्फ इतना है कि मैंने स्थैतिक जाँच को प्राथमिकता दी है। लेकिन आप सही हैं: यह अजगर बिल्कुल नहीं होगा। हालांकि निर्णायक बिंदु नहीं, पायथन 3 के "*" निर्माण को भी गतिशील रूप से जांचा गया है। आपके कमेंट के लिए धन्यवाद।
मारियो रॉसी

इसके अलावा, यदि आप मॉड्यूल चर का नाम देते हैं, तो _named_only_startइसे बाहरी मॉड्यूल से संदर्भित करना असंभव हो जाता है, जो एक प्रो और एक कोन लेता है। (मॉड्यूल के दायरे में एकल अग्रणी अंडरस्कोर निजी हैं, IIRC)
एरिक

प्रहरी के नामकरण के बारे में, हम दोनों ( __named_only_startऔर named_only_startकोई प्रारंभिक अंडरस्कोर) नहीं हो सकते हैं, दूसरा यह इंगित करने के लिए कि नामित मोड "अनुशंसित" है, लेकिन "सक्रिय रूप से प्रचारित" होने के स्तर पर नहीं (जैसा कि एक सार्वजनिक है और है) अन्य नहीं है)। _namesअंडरस्कोर के साथ शुरू होने की "निजता" के बारे में , यह भाषा द्वारा बहुत दृढ़ता से लागू नहीं किया जाता है: इसे विशिष्ट (गैर- *) आयात या योग्य नामों के उपयोग द्वारा आसानी से रोका जा सकता है। यही कारण है कि कई पायथन दस्तावेज "निजी" के बजाय "गैर-सार्वजनिक" शब्द का उपयोग करना पसंद करते हैं।
मारियो रॉसी

6

आप ऐसा एक तरह से कर सकते हैं जो पायथन 2 और पायथन 3 दोनों में काम करता है , एक डिफ़ॉल्ट मूल्य के साथ "फर्जी" पहला कीवर्ड तर्क बनाकर जो "स्वाभाविक रूप से" नहीं होगा। उस खोजशब्द तर्क को मूल्य के बिना एक या अधिक तर्कों से पहले लिया जा सकता है:

_dummy = object()

def info(object, _kw=_dummy, spacing=10, collapse=1):
    if _kw is not _dummy:
        raise TypeError("info() takes 1 positional argument but at least 2 were given")

यह अनुमति देगा:

info(odbchelper)        
info(odbchelper, collapse=0)        
info(spacing=15, object=odbchelper)

लेकिन नहीं:

info(odbchelper, 12)                

यदि आप फ़ंक्शन को इसमें बदलते हैं:

def info(_kw=_dummy, spacing=10, collapse=1):

तब सभी तर्कों में कीवर्ड होने चाहिए और info(odbchelper)वे अब काम नहीं करेंगे।

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

तो def(**kwargs)अपने स्मार्ट एडिटर में हस्ताक्षर की जानकारी का उपयोग करने और खोने के लिए वापस जाने की आवश्यकता नहीं है । आपका सामाजिक अनुबंध कुछ जानकारी प्रदान करने के लिए है, उनमें से कुछ को मजबूर करने के लिए कीवर्ड की आवश्यकता होती है, जिस क्रम में उन्हें प्रस्तुत किया गया है, वह अप्रासंगिक हो गया है।


2

अपडेट करें:

मुझे एहसास हुआ कि उपयोग **kwargsकरने से समस्या हल नहीं होगी। यदि आपके प्रोग्रामर फ़ंक्शन तर्कों को अपनी इच्छानुसार बदलते हैं, तो उदाहरण के लिए, फ़ंक्शन को इसमें बदल सकते हैं:

def info(foo, **kwargs):

और पुराना कोड फिर से टूट जाएगा (क्योंकि अब प्रत्येक फ़ंक्शन कॉल में पहला तर्क शामिल करना होगा)।

यह वास्तव में नीचे आता है जो ब्रायन कहता है।


(...) लोगों के बीच spacingऔर collapse(...) मानकों को जोड़ सकते हैं

सामान्य तौर पर, जब कार्य बदलते हैं, तो नए तर्कों को हमेशा अंत तक जाना चाहिए। अन्यथा यह कोड को तोड़ देता है। स्पष्ट होना चाहिए।
यदि कोई फ़ंक्शन बदलता है ताकि कोड टूट जाए, तो यह परिवर्तन अस्वीकार करना होगा।
(जैसा कि ब्रायन कहते हैं, यह एक अनुबंध की तरह है)

(...) कभी-कभी यह हमेशा स्पष्ट नहीं होता है कि अंदर जाने की क्या जरूरत है।

फ़ंक्शन (यानी def info(object, spacing=10, collapse=1)) के हस्ताक्षर को देखकर तुरंत देखना चाहिए कि प्रत्येक तर्क जिसमें डिफ़ॉल्ट मान नहीं है, अनिवार्य है।
तर्क किस लिए है, डॉकस्ट्रिंग में जाना चाहिए।


पुराना उत्तर (पूर्णता के लिए रखा गया) :

यह शायद एक अच्छा समाधान नहीं है:

आप इस तरह से कार्यों को परिभाषित कर सकते हैं:

def info(**kwargs):
    ''' Some docstring here describing possible and mandatory arguments. '''
    spacing = kwargs.get('spacing', 15)
    obj = kwargs.get('object', None)
    if not obj:
       raise ValueError('object is needed')

kwargsएक ऐसा शब्दकोश है जिसमें कोई भी कीवर्ड तर्क होता है। आप जांच सकते हैं कि क्या एक अनिवार्य तर्क मौजूद है और यदि नहीं, तो एक अपवाद बढ़ाएं।

नकारात्मक पक्ष यह है कि यह अब और स्पष्ट नहीं हो सकता है, जो तर्क संभव है, लेकिन एक उचित डॉकस्ट्रिंग के साथ, यह ठीक होना चाहिए।


3
मुझे आपका पुराना उत्तर बेहतर लगा। केवल इस बात पर टिप्पणी करें कि आप फ़ंक्शन में केवल ** कवर्स स्वीकार क्यों कर रहे हैं। आखिरकार, कोई भी स्रोत कोड में कुछ भी बदल सकता है - आपको अपने निर्णयों के पीछे इरादे और उद्देश्य का वर्णन करने के लिए प्रलेखन की आवश्यकता है।
ब्रैंडन

इस जवाब में कोई वास्तविक जवाब नहीं है!
फिल

2

Python3 के साथ python3 कीवर्ड-केवल तर्क ( *) का अनुकरण किया जा सकता है**kwargs

निम्नलिखित पायथन 3 कोड पर विचार करें:

def f(pos_arg, *, no_default, has_default='default'):
    print(pos_arg, no_default, has_default)

और इसका व्यवहार:

>>> f(1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() takes 1 positional argument but 3 were given
>>> f(1, no_default='hi')
1 hi default
>>> f(1, no_default='hi', has_default='hello')
1 hi hello
>>> f(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() missing 1 required keyword-only argument: 'no_default'
>>> f(1, no_default=1, wat='wat')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() got an unexpected keyword argument 'wat'

यह निम्नलिखित का उपयोग करके सिम्युलेटेड हो सकता है, ध्यान दें कि मैंने "आवश्यक नामित तर्क" मामले में स्विच TypeErrorकरने की स्वतंत्रता ले ली है KeyError, यह बहुत अधिक काम नहीं करेगा कि एक ही अपवाद प्रकार

def f(pos_arg, **kwargs):
    no_default = kwargs.pop('no_default')
    has_default = kwargs.pop('has_default', 'default')
    if kwargs:
        raise TypeError('unexpected keyword argument(s) {}'.format(', '.join(sorted(kwargs))))

    print(pos_arg, no_default, has_default)

और व्यवहार:

>>> f(1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() takes exactly 1 argument (3 given)
>>> f(1, no_default='hi')
(1, 'hi', 'default')
>>> f(1, no_default='hi', has_default='hello')
(1, 'hi', 'hello')
>>> f(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in f
KeyError: 'no_default'
>>> f(1, no_default=1, wat='wat')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in f
TypeError: unexpected keyword argument(s) wat

नुस्खा python3.x में भी समान रूप से काम करता है, लेकिन यदि आपको python3.x केवल


आह, तो kwargs.pop('foo')एक पायथन 2 मुहावरा है? मुझे अपनी कोडिंग शैली को अपडेट करने की आवश्यकता है। मैं अभी भी पायथन 3 🤔
नील

0

आप **argsकेवल प्राप्त करने के रूप में अपने कार्यों की घोषणा कर सकते हैं । यह कीवर्ड तर्क जनादेश देगा, लेकिन आपको यह सुनिश्चित करने के लिए कुछ अतिरिक्त काम करना होगा कि केवल वैध नाम ही पास किए गए हैं।

def foo(**args):
   print args

foo(1,2) # Raises TypeError: foo() takes exactly 0 arguments (2 given)
foo(hello = 1, goodbye = 2) # Works fine.

1
न केवल आपको कीवर्ड जांच को जोड़ना है, बल्कि एक उपभोक्ता के बारे में सोचें जो जानता है कि उन्हें हस्ताक्षर के साथ एक विधि को कॉल करना होगा foo(**kwargs)। मैं उसमें क्या करूँ? foo(killme=True, when="rightnowplease")
डैगरूम

यह वास्तव में निर्भर करता है। विचार करें dict
नौफल इब्राहिम

0

जैसा कि अन्य उत्तर कहते हैं, फ़ंक्शन हस्ताक्षर बदलना एक बुरा विचार है। यदि तर्क डाले जाते हैं, तो अंत में नए पैरामीटर जोड़ें, या हर कॉलर को ठीक करें।

यदि आप अभी भी इसे करना चाहते हैं, तो फंक्शन डेकोरेटर और इंस्पेक्ट.गेटर्जस्पेक फ़ंक्शन का उपयोग करें। इसका उपयोग कुछ इस तरह किया जाएगा:

@require_named_args
def info(object, spacing=10, collapse=1):
    ....

का कार्यान्वयन require_named_args को पाठक के लिए एक अभ्यास के रूप में छोड़ दिया जाता है।

मैं परेशान नहीं होता। हर बार फ़ंक्शन को कॉल करने के बाद यह धीमा हो जाएगा, और आपको कोड लिखने से अधिक सावधानी से बेहतर परिणाम मिलेंगे।


-1

आप **ऑपरेटर का उपयोग कर सकते हैं :

def info(**kwargs):

इस तरह लोग नामित मापदंडों का उपयोग करने के लिए मजबूर होते हैं।


2
और अपने कोड को पढ़े बिना अपने तरीके को कॉल करने का कोई विचार नहीं है, आपके उपभोक्ता पर संज्ञानात्मक भार बढ़ रहा है :(
Dagrooms

उल्लेखित कारण के कारण यह वास्तव में बुरा अभ्यास है और इससे बचा जाना चाहिए।
डेविड एस।

-1
def cheeseshop(kind, *arguments, **keywords):

अजगर में अगर उपयोग * आर्ग्स का अर्थ है कि आप इस पैरामीटर के लिए n-संख्या तर्क पास कर सकते हैं - जो कि एक्सेस करने के लिए फ़ंक्शन के अंदर एक सूची आएगी

और अगर ** kw का उपयोग किया जाता है, तो इसका मतलब है कि इसके खोजशब्द तर्क, जिसे तानाशाही के रूप में एक्सेस किया जा सकता है - आप k-ar args की संख्या को पास कर सकते हैं, और यदि आप उस उपयोगकर्ता को प्रतिबंधित करना चाहते हैं, तो उसे अनुक्रम और तर्कों को क्रम में दर्ज करना होगा तो उपयोग न करें * और ** - (बड़े वास्तु के लिए जेनेरिक समाधान प्रदान करने का इसका पैथोनिक तरीका ...)

यदि आप अपने फ़ंक्शन को डिफ़ॉल्ट मानों के साथ प्रतिबंधित करना चाहते हैं तो आप इसके अंदर जांच कर सकते हैं

def info(object, spacing, collapse)
  spacing = spacing or 10
  collapse = collapse or 1

स्पेसिंग 0 होने की इच्छा होने पर क्या होता है? (उत्तर, आपको 10 मिलते हैं)। यह उत्तर भी उतना ही गलत है, जितना कि अन्य सभी ** kwargs सभी समान कारणों से उत्तर देते हैं।
फिल

-2

मुझे नहीं लगता कि एक प्रोग्रामर पहली जगह में दो अन्य लोगों के बीच एक पैरामीटर क्यों जोड़ेगा।

यदि आप चाहते हैं कि फ़ंक्शन पैरामीटर को नामों के साथ उपयोग किया जाए (उदाहरण के लिए) info(spacing=15, object=odbchelper) ) तो इससे कोई फर्क नहीं पड़ता कि उन्हें किस क्रम में परिभाषित किया गया है, इसलिए आप अंत में नए पैरामीटर डाल सकते हैं।

यदि आप आदेश देना चाहते हैं तो आप इसे बदलने के लिए कुछ भी काम करने की उम्मीद नहीं कर सकते हैं!


2
इस सवाल का जवाब नहीं है। एक अच्छा विचार अप्रासंगिक है या नहीं - कोई भी इसे कर सकता है।
ग्रीम पेरो

1
जैसा कि ग्रीम ने उल्लेख किया है, कोई इसे वैसे भी करेगा। इसके अलावा, यदि आप दूसरों द्वारा उपयोग किए जाने के लिए एक पुस्तकालय लिख रहे हैं, तो जबरदस्ती (केवल पायथन 3) कीवर्ड का तर्क केवल तर्क को अतिरिक्त लचीलापन देता है जब आपको अपने एपीआई को रिफलेक्टर करना पड़ता है।
s0undt3ch
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.