programing tip

Python : 선택적 함수 매개 변수가 설정되었는지 확인하는 방법

itbloger 2020. 11. 21. 14:35
반응형

Python : 선택적 함수 매개 변수가 설정되었는지 확인하는 방법


선택적 매개 변수의 값이 기본값에서 오는지 또는 사용자가 함수 호출에서 명시 적으로 설정했기 때문에 Python에서 쉽게 확인할 수있는 방법이 있습니까?


많은 답변에 전체 정보의 작은 부분이 있으므로 내가 가장 좋아하는 패턴과 함께 모두 가져오고 싶습니다.

기본값은 mutable유형입니다.

기본값이 변경 가능한 객체 인 경우 운이 좋습니다. 함수가 정의 될 때 Python의 기본 인수가 한 번 평가된다는 사실을 이용할 수 있습니다 (답변 끝에 이것에 대해 더 자세히 설명합니다)

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 제안과 유사하게 파이썬 문서가 충분하지 않은 경우 :-) 문서를 읽지 않고 API를 더 명시 적으로 만들기 위해 그 사이에 객체를 추가 할 수 있습니다.

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에 의해 제안 패턴을, 그러나 이것은 여전히 함수 정의 자체가 이상한 트릭을 사용하도록 강요합니다 valuevalue_default그래서, 당신의 코드를 필요 구별하는 경우 그 나는 많은 이점을 보지 못하고 예제를 쓰지 않을 것입니다 :-)

기본값으로 변경 가능한 유형

# 1 python gotcha 에 대해 조금 더 ! , 위의 자신의 즐거움을 위해 학대 받았습니다. 다음을 수행하여 정의 에서 평가 로 인해 어떤 일이 발생하는지 확인할 수 있습니다 .

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

원하는 testme()만큼 실행할 수 있으며 항상 동일한 기본 인스턴스에 대한 참조를 볼 수 있습니다 (기본적으로 기본값은 변경 불가능합니다 :-)).

파이썬에 있다는 것을 기억하십시오 3 변경 가능한 내장 유형 : set, list, dict,하지만 다른 모든 것들 - 심지어 문자열! -불변입니다.


별로. 표준 방법은 사용자가 전달할 것으로 예상되지 않는 기본값 ( 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매개 변수가 자동으로 포함되지 않으므로 함수를 사용하기가 더 어려워집니다 .


다음 함수 데코레이터 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)

때때로 UUID와 같은 보편적으로 고유 한 문자열을 사용합니다.

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

이렇게하면 어떤 사용자도 시도해도 기본값을 추측 할 수 없으므로에 대한 값을 볼 때 arg전달되지 않았 음을 확신 할 수 있습니다 .


Volatility의 의견에 동의합니다. 그러나 다음과 같은 방식으로 확인할 수 있습니다.

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

나는이 패턴을 본 적이 몇 번 (예를 들어, 도서관 unittest, py-flags, jinja) :

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

DEFAULT = Default()

... 또는 이에 상응하는 한 줄짜리 ... :

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

Unlike DEFAULT = object(), this assists type-checking and provides information when errors occur -- frequently either the string representation ("DEFAULT") or the class name ("Default") are used in error messages.


You can check it from foo.__defaults__ and foo.__kwdefaults__

see a simple example bellow

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

then by using operator = and is you can compare them

and for some cases code bellow is enought

For example, you need to avoid changing default value then you can check on equality and then copy if so

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

A little freakish approach would be:

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()

Which outputs:

passed default
z
passed different
b
not passed
z

Now this, as I mentioned, is quite freakish, but it does the job. However this is quite unreadable and similarly to ecatmur's suggestion won't be automatically documented.


@Ellioh's answer works in python 2. In python 3, the following code should work:

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

This method can keep the argument names and default values (instead of **kwargs) with better readability.

참고URL : https://stackoverflow.com/questions/14749328/python-how-to-check-whether-optional-function-parameter-is-set

반응형