Checkচ্ছিক ফাংশন প্যারামিটার সেট করা আছে কিনা তা কীভাবে পরীক্ষা করবেন


91

পাইথনে কোনও wayচ্ছিক প্যারামিটারের মানটি তার ডিফল্ট মান থেকে আসে কিনা বা ব্যবহারকারী ফাংশন কলে এটিকে সুস্পষ্টভাবে সেট করেছে কিনা তা যাচাই করার জন্য কি সহজ উপায় আছে?


12
কারণ আমি অবশ্যই সেই ফাংশনটিতে এটি যাচাই করতে চাই :)
ম্যাথিয়া

4
কেবলমাত্র Noneডিফল্ট হিসাবে ব্যবহার করুন এবং এটি পরীক্ষা করুন check আপনি যদি সত্যিই এই পরীক্ষাটি সেট আপ করতে পারতেন তবে আপনি ডিফল্ট আচরণের জন্য অনুরোধ করে এমন মানটি স্পষ্টভাবে পাস করার জন্য কোনও সম্ভাবনাও বাদ দিতেন।
মাইকেল জে বারবার

4
যা আপনি স্বীকার করেছেন, তার চেয়ে কমপক্ষে সিপিথনের চেয়ে অনেক বেশি পুনরায় ব্যবহারযোগ্য এবং সুন্দর উপায়ে করা যেতে পারে। আমার উত্তর নীচে দেখুন।
এলিওহ

4
@ ভোলাটিলিটি: আপনার যদি দুটি সেট ডিফল্ট থাকে তবে তা গুরুত্বপূর্ণ। একটি পুনরাবৃত্ত শ্রেণীর বিবেচনা করুন: Class My(): def __init__(self, _p=None, a=True, b=True, c=False) ব্যবহারকারী এটির সাথে কল করে x=My(b=False)। একটি শ্রেণি পদ্ধতি x=My(_p=self, c=True)যদি ফাংশনগুলি সনাক্ত করতে পারে যে খ স্পষ্টভাবে সেট করা হয়নি এবং সেট না করে থাকা ভেরিয়েবলগুলি শীর্ষ স্তর থেকে নীচে নেমে যেতে পারে তবে একটি শ্রেণি পদ্ধতি নিজেকে কল করতে পারে। কিন্তু যদি তারা পারে না, recursive কল যে পরিবর্তনশীল স্পষ্টভাবে পাস করতে হবে: x=My(a=self.a, b=self.b, c=True, d=self.d, ...)
ডেভ

@ ডেভ তবে প্রশ্নটি কী? আমার বোঝার মধ্যে, প্রশ্নটি কীভাবে আলাদা করতে হবে x=My()এবং জিজ্ঞাসা করছে x=My(a=True)। আপনার দৃশ্যে defaultচ্ছিক প্যারামিটারগুলি তাদের ডিফল্ট মান বাদে অন্য কোনও মান নির্ধারণ করে।
অস্থিরতা

উত্তর:


16

প্রচুর উত্তরের সম্পূর্ণ তথ্যের সামান্য অংশ রয়েছে, তাই আমি এগুলি আমার প্রিয় প্যাটার্নগুলির সাথে একত্রিত করতে চাই।

ডিফল্ট মান একটি 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 পরামর্শের অনুরূপ, যদি পাইথন ডক্স পর্যাপ্ত না থাকে :-), আপনি এপিআইকে আরও স্পষ্ট করে তুলতে (ডক্সগুলি না পড়ে) এর মধ্যে একটি বস্তু যুক্ত করতে পারেন; ব্যবহৃত_প্রক্সি_ ডিফল্ট শ্রেণীর উদাহরণটি পরিবর্তনযোগ্য এবং এতে আপনি ব্যবহার করতে চান এমন আসল ডিফল্ট মান থাকবে।

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, যা অপরিবর্তনীয় হওয়া উচিত ...
স্টেফানো

আমি হিসাবে ফাংশন স্বাক্ষর দেখুন def f(value={})
করোল

@ কারোল এটি স্বাক্ষর, তবে পছন্দসই ডিফল্ট মান 1; দুঃখিত যদি তা ব্যাখ্যায় পরিষ্কার না হয় তবে প্রতিক্রিয়াটির সেই অংশটির পুরো পয়েন্টটি একটি অপরিবর্তনীয় ডিফল্ট ( 1) রাখতে সক্ষম হবে । আপনি যদি উদাহরণটি পরীক্ষা করেন, আপনি দেখতে পাবেন এটি বলেছেন: print('default'); value = 1না,value={}
স্টেফানো

4
হা, আমি এখন এটি পেয়েছি, ধন্যবাদ। কেউ আপনার পাঠ্য খুব মনোযোগ সহকারে না পড়লে তা অনুসরণ করা সহজ নয় যা এসওতে প্রায়শই ঘটে না। পুনর্নির্মাণ বিবেচনা করুন।
করোল

56

আসলে তা না. স্ট্যান্ডার্ড উপায় হ'ল একটি ডিফল্ট মান ব্যবহার করা যা ব্যবহারকারীর দ্বারা প্রত্যাশিত হয় না যেমন 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প্যারামিটারটি স্বয়ংক্রিয়ভাবে অন্তর্ভুক্ত হবে না ।


8
আমি বেশিরভাগ লোককে এলিপসিস বিল্টিন ব্যবহার করে এমন জায়গাগুলির জন্যও দেখেছি যেখানে এটির প্রয়োজন হয় এবং কোনওটিই বৈধ ইনপুট হিসাবে বিবেচিত হয় না। এটি প্রথম উদাহরণ হিসাবে মূলত একই।
গ্র্যান্ডওপেনার

যদি কোনওটি পাস না করা হয় তবে আপনি যদি বিশেষ আচরণ বাস্তবায়ন করতে চান তবে তবুও এটির পরীক্ষার কোনও উপায় প্রয়োজন যদি ব্যবহারকারী দ্বারা যুক্তিটি দেওয়া হয়, আপনি Ellipsisসিঙ্গলটনকে ডিফল্ট হিসাবে ব্যবহার করতে পারেন , যা স্পষ্টভাবে এই মানটিকে এড়িয়ে যেতে ব্যবহৃত হয়েছিল designed এটির ...জন্য একটি উপনাম 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সুস্পষ্টভাবে দেওয়া হয়েছে কিনা তা যাচাই করতে হবে do

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)

এই কোডটি কেবল পাইথন 2 এ কাজ করে। অজগর 3 এর জন্য, নীচে আমার উত্তরটি দেখুন: stackoverflow.com/questions/14749328/…
আর ইয়াং

4
এটি বেশ দুর্দান্ত, তবে যদি সম্ভব হয় তবে প্রথমে আরও ভাল ডিজাইনের সমস্যাটি এড়ানো ভাল।
করোল 21

@ কারোল, আমি একমত বেশিরভাগ ক্ষেত্রে যদি প্রয়োজন হয় না যে নকশাটি যুক্তিসঙ্গত হয়।
এলিওহ

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

@ এলিওয়ের উত্তর পাইথন ২ তে কাজ করে works অজগর 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

এই পদ্ধতিটি আরও ভাল পঠনযোগ্যতার সাথে যুক্তির নাম এবং ডিফল্ট মানগুলি (** কোয়ার্গসের পরিবর্তে) রাখতে পারে।


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আপনি সেগুলি তুলনা করতে পারেন

এবং কিছু ক্ষেত্রে কোড বেলো করা হয়

উদাহরণস্বরূপ, আপনাকে ডিফল্ট মান পরিবর্তন করা এড়াতে হবে তারপরে আপনি সমতাটি পরীক্ষা করতে পারেন এবং তারপরে যদি তা অনুলিপি করেন

    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
alচ্ছিক

এটি এমন কিছু যা ব্যাখ্যার জন্য কিছুটা উন্মুক্ত। একটি ডিফল্ট মান এবং কীওয়ার্ড আর্গুমেন্টের সাথে একটি আর্গুমেন্টের মধ্যে আসল পার্থক্য কী? তারা উভয়ই একই সিনট্যাক্স "কীওয়ার্ড = মান" ব্যবহার করে প্রকাশ করা হয়।
ইসেদেভ

আমি অসমত, কারণ becauseচ্ছিক পরামিতিগুলির উদ্দেশ্য এবং **kwargsকিছুটা আলাদা bit পিএস কোনও সমস্যা নেই -1 :) এবং আপনার জন্য আমার -1 দুর্ঘটনাক্রমে ছিল :)
জৌর নাসিভভ

2

এটি স্টেফানোর উত্তরের একটি ভিন্নতা, তবে আমি আরও কিছুটা পাঠযোগ্য find

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

এখন এটি, যেমনটি আমি উল্লেখ করেছি, বেশ উদ্ভট, তবে এটি কাজটি করে। তবে এই বেশ অপাঠ্য এবং একইভাবে করার ecatmur এর প্রস্তাবনা স্বয়ংক্রিয়ভাবে নথিভুক্ত করা হবে না।


4
আপনি check_f('z')যেমন ব্যবহার করেছেন তেমন আচরণকে অন্তর্ভুক্ত করতে চাইতে পারেন fre
মাইকেল জে বারবার

@ মাইকেল জে.বারবার ভাল পয়েন্ট আপনাকে * আরগস দিয়েও কিছু "যাদু" করতে হবে। যাইহোক, আমার বক্তব্যটি ছিল এটি সম্ভব, তবে এখনই প্রয়োজন ডিফল্ট মানটি পাস হয়েছে কিনা তা একটি খারাপ নকশা।
dmg
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.