कैसे जांचें कि वैकल्पिक फ़ंक्शन पैरामीटर सेट है या नहीं


93

क्या पायथन में एक आसान तरीका है यह जांचने के लिए कि क्या वैकल्पिक पैरामीटर का मूल्य उसके डिफ़ॉल्ट मूल्य से आता है, या क्योंकि उपयोगकर्ता ने फ़ंक्शन कॉल पर इसे स्पष्ट रूप से सेट किया है?


12
क्योंकि मैं इसे पाठ्यक्रम के उस फंक्शन में जाँचना चाहता हूँ :)
मथायस

2
बस Noneडिफ़ॉल्ट के रूप में उपयोग करें और उसके लिए जांचें। यदि आप वास्तव में इस परीक्षण को सेट कर सकते हैं, तो आप उपयोगकर्ता के लिए डिफ़ॉल्ट व्यवहार को स्पष्ट करने वाले मान को स्पष्ट रूप से पारित करने की किसी भी संभावना को छोड़ देंगे।
माइकल जे। नाईट

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

2
@Volatility: यदि आपके पास चूक के दो सेट हैं तो यह मायने रखता है। एक पुनरावर्ती वर्ग पर विचार करें: Class My(): def __init__(self, _p=None, a=True, b=True, c=False) उपयोगकर्ता इसे कहता है x=My(b=False)। एक क्लास मेथड अपने आप को कॉल कर सकता है x=My(_p=self, c=True)यदि फ़ंक्शन यह पता लगा सकते हैं कि बी स्पष्ट रूप से सेट नहीं है और शीर्ष स्तर से परेशान वेरिएबल्स को पारित किया जाना है। लेकिन अगर वे नहीं कर सकते हैं, तो पुनरावर्ती कॉल को प्रत्येक चर को स्पष्ट रूप से पास करना होगा x=My(a=self.a, b=self.b, c=True, d=self.d, ...):।
डेव

@ लेकिन क्या यह सवाल है? मेरी समझ में, सवाल यह है कि कैसे अंतर करना है x=My()और x=My(a=True)। आपके परिदृश्य में वैकल्पिक पैरामीटर को उनके डिफ़ॉल्ट मान के अलावा एक मान निर्दिष्ट करना शामिल है।
अस्थिरता

जवाबों:


19

उत्तर की बहुत सारी जानकारी के बहुत कम टुकड़े हैं, इसलिए मैं इसे अपने पसंदीदा पैटर्न (ओं) के साथ लाना चाहता हूं।

डिफ़ॉल्ट मान एक mutableप्रकार है

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

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

def f(value={}):
    if value is f.__defaults__[0]:
        print('default')
    else:
        print('passed in the call')

तथा

class A:
    def f(self, value={}):
        if value is self.f.__defaults__[0]:
            print('default')
        else:
            print('passed in the call')

अपरिवर्तनीय डिफ़ॉल्ट तर्क

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

def f(value={}):
    """
    my function
    :param value: value for my function; default is 1
    """
    if value is f.__defaults__[0]:
        print('default')
        value = 1
    else:
        print('passed in the call')
    # whatever I want to do with the value
    print(value)

यदि आप वास्तविक डिफ़ॉल्ट हैं तो यह विशेष रूप से मज़ेदार लगता है None, लेकिन Noneअपरिवर्तनीय है इसलिए ... आपको अभी भी फ़ंक्शन डिफ़ॉल्ट पैरामीटर के रूप में स्पष्ट रूप से एक उत्परिवर्ती का उपयोग करने की आवश्यकता है, और कोड में कोई भी नहीं स्विच करें।

Defaultअपरिवर्तनीय चूक के लिए एक वर्ग का उपयोग करना

या, @cz सुझाव के समान, यदि अजगर डॉक्स पर्याप्त नहीं हैं :-), तो आप एपीआई को अधिक स्पष्ट करने के लिए (डॉक्स को पढ़े बिना) बनाने के लिए बीच में एक ऑब्जेक्ट जोड़ सकते हैं; used_proxy_ डिफ़ॉल्ट श्रेणी का उदाहरण परिवर्तनशील है, और इसमें वह वास्तविक डिफ़ॉल्ट मान होगा जिसका आप उपयोग करना चाहते हैं।

class Default:
    def __repr__(self):
        return "Default Value: {} ({})".format(self.value, type(self.value))

    def __init__(self, value):
        self.value = value

def f(default=Default(1)):
    if default is f.__defaults__[0]:
        print('default')
        print(default)
        default = default.value
    else:
        print('passed in the call')
    print("argument is: {}".format(default))

अभी:

>>> f()
default
Default Value: 1 (<class 'int'>)
argument is: 1

>>> f(2)
passed in the call
argument is: 2

उपरोक्त अच्छी तरह से भी काम करता है Default(None)

अन्य पैटर्न

जाहिर है कि उपरोक्त पैटर्न उन सभी की वजह से बदसूरत दिखते printहैं , जो केवल यह दिखाने के लिए हैं कि वे कैसे काम करते हैं। अन्यथा मैं उन्हें पर्याप्त और दोहरावदार पाता हूं।

आप __call__@dmg द्वारा सुझाए गए पैटर्न को अधिक सुव्यवस्थित तरीके से जोड़ने के लिए एक डेकोरेटर लिख सकते हैं , लेकिन यह अभी भी फ़ंक्शन परिभाषा में अजीब चाल का उपयोग करने के लिए बाध्य होगा - आपको इसे अलग करने की आवश्यकता होगी valueऔर value_defaultयदि आपके कोड को उन्हें अलग करने की आवश्यकता है, तो मुझे बहुत फायदा नहीं दिख रहा है और मैं उदाहरण नहीं लिखूंगा :-)

पायथन में डिफ़ॉल्ट मानों के रूप में पारस्परिक प्रकार

# 1 अजगर गोथा के बारे में थोड़ा और अधिक ! , ऊपर अपने आनंद के लिए गाली दी। आप यह देख सकते हैं कि मूल्यांकन के कारण क्या होता है :

def testme(default=[]):
    print(id(default))

आप testme()जितनी बार चाहें दौड़ सकते हैं, आप हमेशा एक ही डिफ़ॉल्ट उदाहरण के संदर्भ देखेंगे (इसलिए मूल रूप से आपका डिफ़ॉल्ट अपरिवर्तनीय है :-))।

याद रखें अजगर में देखते हैं कि केवल 3 परिवर्तनशील में निर्मित प्रकार : set, list, dict, बाकी सब कुछ - यहां तक ​​कि तार! - अपरिवर्तनीय है।


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

@ कैरोल, विस्तृत करने के लिए देखभाल? कि उदाहरण में डिफ़ॉल्ट मान है 1जो अपरिवर्तनीय होना चाहिए, ...
Stefano

मैं फ़ंक्शन के हस्ताक्षर को देखता हूं def f(value={})
करोल

1
हा, मैं इसे अभी प्राप्त करता हूं, धन्यवाद। जब तक कोई आपके पाठ को बहुत सावधानी से नहीं पढ़ता है, तब तक इसका पालन करना आसान नहीं होता है जो कि एसओ पर अक्सर नहीं होता है। रेकॉर्डिंग पर विचार करें।
करोल

1
"अगर डिफॉल्ट f .__ डिफॉल्ट्स __ [0]:" है, तो आपको हार्ड-कोड का उपयोग करना होगा जो डिफॉल्ट पैरामीटर नंबर का उपयोग करता है, जो कि फंक्शन सिग्नेचर में बदलाव होने पर नाजुक हो सकता है। एक विकल्प है "यदि डिफ़ॉल्ट में। F डिफॉल्ट__:"। यह मानते हुए कि आप प्रत्येक arg के लिए एक अलग डिफ़ॉल्ट उदाहरण का उपयोग करते हैं, "में काम करना चाहिए और साथ ही" है "।
स्टीफन वॉरेन

57

ज़रुरी नहीं। मानक तरीका एक डिफ़ॉल्ट मान का उपयोग करना है जिसे उपयोगकर्ता को पास करने की उम्मीद नहीं होगी, उदाहरण के objectलिए:

DEFAULT = object()
def foo(param=DEFAULT):
    if param is DEFAULT:
        ...

आमतौर पर आप बस Noneडिफ़ॉल्ट मान के रूप में उपयोग कर सकते हैं , अगर यह समझ में नहीं आता है कि उपयोगकर्ता किस मूल्य को पारित करना चाहता है।

विकल्प का उपयोग करना है kwargs:

def foo(**kwargs):
    if 'param' in kwargs:
        param = kwargs['param']
    else:
        ...

हालाँकि यह अत्यधिक क्रिया है और आपके फ़ंक्शन को उपयोग करने में अधिक कठिन बनाता है क्योंकि इसके प्रलेखन में स्वचालित रूप से paramपैरामीटर शामिल नहीं होगा ।


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

यदि आप विशेष व्यवहार को लागू करना चाहते हैं यदि कोई भी पारित नहीं किया गया था, लेकिन फिर भी परीक्षण करने के लिए एक तरीके की आवश्यकता है यदि तर्क उपयोगकर्ता द्वारा दिया गया था, तो आप Ellipsisएक डिफ़ॉल्ट के रूप में सिंगलटन का उपयोग कर सकते हैं , जिसे स्पष्ट रूप से इस मान को छोड़ें के रूप में उपयोग करने के लिए डिज़ाइन किया गया था । ...के लिए एक उपनाम है Ellipsis, इसलिए जो उपयोगकर्ता स्थिति-संबंधी तर्कों का उपयोग करना चाहते हैं, वे केवल कॉल कर सकते हैं your_function(p1, ..., p3)जो पढ़ने में स्पष्ट और अच्छा लगता है।
बछसौ

However this is overly verbose and makes your function more difficult to use as its documentation will not automatically include the param parameter.यह वास्तव में असत्य है, क्योंकि आप inspectमॉड्यूल का उपयोग करके किसी फ़ंक्शन और उसके मापदंडों का विवरण सेट कर सकते हैं । यह आपके आईडीई पर निर्भर करता है कि वह काम करेगा या नहीं।
EZLearner

15

निम्न फ़ंक्शन डेकोरेटर, explicit_checkerस्पष्ट रूप से दिए गए सभी मापदंडों के पैरामीटर नामों का एक सेट बनाता है। यह explicit_paramsफ़ंक्शन में एक अतिरिक्त पैरामीटर ( ) के रूप में परिणाम जोड़ता है । बस 'a' in explicit_paramsजाँच करें कि क्या पैरामीटर aस्पष्ट रूप से दिया गया है।

def explicit_checker(f):
    varnames = f.func_code.co_varnames
    def wrapper(*a, **kw):
        kw['explicit_params'] = set(list(varnames[:len(a)]) + kw.keys())
        return f(*a, **kw)
    return wrapper

@explicit_checker
def my_function(a, b=0, c=1, explicit_params=None):
    print a, b, c, explicit_params
    if 'b' in explicit_params:
        pass # Do whatever you want


my_function(1)
my_function(1, 0)
my_function(1, c=1)

यह कोड केवल python2 में काम करता है। अजगर 3 के लिए, नीचे मेरा जवाब देखें: stackoverflow.com/questions/14749328/…
आर। यांग

1
यह बहुत अच्छा है, लेकिन पहले से बेहतर डिजाइन के साथ समस्या से बचने के लिए बेहतर है, यदि संभव हो तो।
करोल

@ करोल, मैं सहमत हूं। ज्यादातर मामलों में किसी को इसकी आवश्यकता नहीं होनी चाहिए कि क्या डिजाइन उचित है।
एलिओह

4

मैं कभी-कभी एक सार्वभौमिक रूप से अद्वितीय स्ट्रिंग (जैसे कि यूयूआईडी) का उपयोग करता हूं।

import uuid
DEFAULT = uuid.uuid4()
def foo(arg=DEFAULT):
  if arg is DEFAULT:
    # it was not passed in
  else:
    # it was passed in

इस तरह, कोई भी उपयोगकर्ता डिफ़ॉल्ट का अनुमान नहीं लगा सकता है अगर उन्होंने कोशिश की तो मुझे बहुत विश्वास हो सकता है कि जब मैं उस मूल्य को देखता हूं arg, तो यह पारित नहीं हुआ था।


4
पायथन ऑब्जेक्ट्स संदर्भ हैं, आप object()इसके बजाय बस का उपयोग कर सकते हैं uuid4()- यह अभी भी एक अनूठा उदाहरण है , जो कि isजाँच करता है
cz

3

मैं इस पैटर्न देखा है कई बार (जैसे पुस्तकालय unittest, py-flags, jinja):

class Default:
    def __repr__( self ):
        return "DEFAULT"

DEFAULT = Default()

... या समकक्ष एक-लाइनर ...:

DEFAULT = type( 'Default', (), { '__repr__': lambda x: 'DEFAULT' } )()

इसके विपरीत DEFAULT = object(), यह टाइप-चेकिंग की सहायता करता है और त्रुटियां होने पर जानकारी प्रदान करता है - अक्सर त्रुटि संदेशों में स्ट्रिंग प्रतिनिधित्व ( "DEFAULT") या वर्ग नाम ( "Default") का उपयोग किया जाता है।


3

@ एलियोह का जवाब अजगर 2 में काम करता है। अजगर 3 में, निम्नलिखित कोड काम करना चाहिए:

import inspect
def explicit_checker(f):
    varnames = inspect.getfullargspec(f)[0]
    def wrapper(*a, **kw):
        kw['explicit_params'] = set(list(varnames[:len(a)]) + list(kw.keys()))
        return f(*a, **kw)
    return wrapper

@explicit_checker
def my_function(a, b=0, c=1, explicit_params=None):
    print a, b, c, explicit_params
    if 'b' in explicit_params:
        pass # Do whatever you want

यह विधि तर्क नामों और डिफ़ॉल्ट मानों (बजाय ** kwargs) को बेहतर पठनीयता के साथ रख सकती है।


3

आप इसे foo.__defaults__और से देख सकते हैंfoo.__kwdefaults__

एक साधारण उदाहरण देखें

def foo(a, b, c=123, d=456, *, e=789, f=100):
    print(foo.__defaults__)
    # (123, 456) 
    print(foo.__kwdefaults__)
    # {'e': 789, 'f': 100}
    print(a, b, c, d, e, f)

#and these variables are also accessible out of function body
print(foo.__defaults__)    
# (123, 456)  
print(foo.__kwdefaults__)  
# {'e': 789, 'f': 100}

foo.__kwdefaults__['e'] = 100500

foo(1, 2) 
#(123, 456)
#{'f': 100, 'e': 100500}
#1 2 123 456 100500 100

फिर ऑपरेटर का उपयोग करके =और isआप उनकी तुलना कर सकते हैं

और कुछ मामलों के लिए कोड bellow पर्याप्त है

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

    def update_and_show(data=Example):
        if data is Example:
            data = copy.deepcopy(data)
        update_inplace(data) #some operation
        print(data)

इसके अलावा, यह काफी सुविधाजनक होता है getcallargsसे inspectके रूप में यह वास्तविक तर्क है जिसके साथ समारोह द्वारा सक्रिय किया जाएगा देता है। आप एक फंक्शन पास करते हैं और उसके पास आर्ग और कर्व्स ( inspect.getcallargs(func, /, *args, **kwds)) करते हैं, यह वास्तविक विधि के तर्कों को लौटाएगा, जो कि डिक्लोकेशन के लिए उपयोग किया जाता है, डिफ़ॉल्ट मान और अन्य सामान को ध्यान में रखते हुए। नीचे दिए गए उदाहरण पर एक नज़र डालें।

from inspect import getcallargs

# we have a function with such signature
def show_params(first, second, third=3):
    pass

# if you wanted to invoke it with such params (you could get them from a decorator as example)
args = [1, 2, 5]
kwargs = {}
print(getcallargs(show_params, *args, **kwargs))
#{'first': 1, 'second': 2, 'third': 5}

# here we didn't specify value for d
args = [1, 2, 3, 4]
kwargs = {}

# ----------------------------------------------------------
# but d has default value =7
def show_params1(first, *second, d = 7):
    pass


print(getcallargs(show_params1, *args, **kwargs))
# it will consider b to be equal to default value 7 as it is in real method invocation
# {'first': 1, 'second': (2, 3, 4), 'd': 7}

# ----------------------------------------------------------
args = [1]
kwargs = {"d": 4}

def show_params2(first, d=3):
    pass


print(getcallargs(show_params2, *args, **kwargs))
#{'first': 1, 'd': 4}

https://docs.python.org/3/library/inspect.html


2

मैं अस्थिरता की टिप्पणी से सहमत हूं। लेकिन आप निम्नलिखित तरीके से जाँच कर सकते हैं:

def function(arg1,...,**optional):
    if 'optional_arg' in optional:
        # user has set 'optional_arg'
    else:
        # user has not set 'optional_arg'
        optional['optional_arg'] = optional_arg_default_value # set default

मेरा मानना ​​है कि एक वैकल्पिक पैरामीटर कुछ ऐसा def func(optional=value)नहीं है**kwargs
Zaur Nasibov

यह कुछ ऐसा है जो व्याख्या के लिए कुछ हद तक खुला है। डिफ़ॉल्ट मान और कीवर्ड तर्क के साथ तर्क के बीच वास्तविक अंतर क्या है? वे दोनों एक ही वाक्यविन्यास "कीवर्ड = मान" का उपयोग करके व्यक्त किए जाते हैं।
isedev

मैं असहमत हूं, क्योंकि वैकल्पिक मापदंडों का उद्देश्य और **kwargsथोड़ा अलग है। PS कोई समस्या नहीं -1 के बारे में :) और मेरे -1 आपके लिए आकस्मिक था :)
ज़ौर नसीबोव

2

यह स्टेफानो के उत्तर पर एक भिन्नता है, लेकिन मुझे थोड़ा और पढ़ने योग्य लगता है:

not_specified = {}

def foo(x=not_specified):
    if x is not_specified:
            print("not specified")
    else:
            print("specified")

एक उत्थान ?? मुझे यह सबसे अच्छा लगता है। सरल, कोई प्रतिबिंब नहीं। पठनीय।
विन्सेंट

1

थोड़ा अजीब दृष्टिकोण होगा:

class CheckerFunction(object):
    def __init__(self, function, **defaults):
        self.function = function
        self.defaults = defaults

    def __call__(self, **kwargs):
        for key in self.defaults:
            if(key in kwargs):
                if(kwargs[key] == self.defaults[key]):
                    print 'passed default'
                else:
                    print 'passed different'
            else:
                print 'not passed'
                kwargs[key] = self.defaults[key]

        return self.function(**kwargs)

def f(a):
    print a

check_f = CheckerFunction(f, a='z')
check_f(a='z')
check_f(a='b')
check_f()

कौन से आउटपुट:

passed default
z
passed different
b
not passed
z

अब, जैसा कि मैंने उल्लेख किया है, काफी अजीब है, लेकिन यह काम करता है। हालांकि यह काफी अपठनीय है और इसी तरह से ईटमाटुर के सुझाव को स्वचालित रूप से प्रलेखित नहीं किया जाएगा।


1
आप के व्यवहार को शामिल करना चाह सकते हैं check_f('z'), जो कि जैसा कि आप कहते हैं, अजीब है।
माइकल जे। नाईट

@ माइकलजे.बर्बर गुड पॉइंट। आपको * args के साथ कुछ "जादू" भी करना होगा। हालाँकि, मेरा कहना यह था कि यह संभव है, लेकिन अब इस बात की आवश्यकता है कि क्या डिफ़ॉल्ट मान पारित किया गया है या नहीं यह एक बुरा डिज़ाइन है।
dmg
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.