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에 의해 제안 패턴을, 그러나 이것은 여전히 함수 정의 자체가 이상한 트릭을 사용하도록 강요합니다 value
및 value_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.
'programing tip' 카테고리의 다른 글
거의 정렬 된 배열 정렬 (k 이하로 잘못 배치 된 요소) (0) | 2020.11.21 |
---|---|
작성 및 업데이트시 MySQL CURRENT_TIMESTAMP (0) | 2020.11.21 |
ES6 템플릿 리터럴이 문자열 연결보다 빠릅니까? (0) | 2020.11.21 |
Tensorflow에서 Poolallocator 메시지를 해석하는 방법은 무엇입니까? (0) | 2020.11.21 |
다른 요소 앞에 오는 요소를 선택하기위한 CSS 선택기? (0) | 2020.11.21 |