Есть простой способ в Python, чтобы проверить, является ли значение необязательного параметра исходит из его значения по умолчанию, или потому, что пользователь установил его прямо на вызове функции?
Не правда. Стандартный способ-использовать значение по умолчанию, что пользователь не будет ожидать, чтобы пройти, например, "объект" экземпляр:
DEFAULT = object()
def foo(param=DEFAULT):
if param is DEFAULT:
...
Обычно вы можете просто использовать " нет " в качестве значения по умолчанию, если это не'т иметь смысл как значение, которое пользователь захочет пройти.
Альтернативой является использование kwargs
:
def foo(**kwargs):
if 'param' in kwargs:
param = kwargs['param']
else:
...
Однако это чрезмерно многословен и делает вашу функцию более трудно использовать в качестве документации не будет автоматически включен параметр парам
.
Следующая функция-декоратор, explicit_checker
, производит набор названий параметров всех параметров в явном виде. Он добавляет результат в качестве дополнительного параметра (explicit_params
) к функции. Просто сделать `'Это' в explicit_params, чтобы проверить, если параметр " а " в явном виде.
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
Таким образом, ни один пользователь не мог даже догадаться, по умолчанию, если они попробовали, поэтому я могу быть очень уверены в том, что когда я вижу, что значение арг
, он не был принят.
Много ответов есть маленькие кусочки полная информация, поэтому я'd, как, чтобы принести все это вместе с моим любимым рисунком(ы).
Если значением по умолчанию является изменяемый объект, то вам повезло: вы можете воспользоваться тем, что аргументы по умолчанию в Python несколько раз, когда функция определена (подробнее об этом в конце ответа)
Это означает, что вы можете легко сравнить мутабельное значение по умолчанию с помощью "есть", чтобы увидеть, если он был передан в качестве аргумента или оставил по умолчанию, как показано в следующих примерах в качестве функции или метода:
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')
Теперь, это'немного менее элегантно, если по умолчанию предполагается, неизменный
значение (и помните, что даже как строки являются неизменными!) потому что вы можете'т использовать трюк, как это, но вы все еще можете сделать что-то вроде:
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)
Он чувствует себя особенно смешно, если вы реально по умолчанию нет
, но никто
является неизменным, так что...
или, аналогично @с-з предложение, если Python документов не хватает :-) , вы можете добавить объект между ними, чтобы сделать 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
Выше прекрасно работает и по умолчанию(нет)`.
Вполне очевидно, что узор выглядит уродливее, чем они должны из-за всех "печать", которые существуют только для показа, как они работают. В противном случае я нахожу их достаточно лаконична и повторяемым.
Вы могли бы написать декоратора, чтобы добавить __звоните__
картины полагают @DMG в более рациональный способ, но это по-прежнему обязаны использовать странные методы, представленные в функции определения себя - вы должны были бы разделить значение
и value_default
если код нужно их различать, так что я Дон'т см. Большое преимущество, и я выиграл'т писать например :-)
Немного больше о #1 питон попался!, использовался для собственных выше удовольствия. Вы можете увидеть, что происходит за счет evaluation на definition, выполнив:
def testme(default=[]):
print(id(default))
Вы можете выполнить достижения этой цели()` столько времени, сколько вы хотите, вы всегда будете видеть ссылку на тот же экземпляр по умолчанию (так в основном по умолчанию является неизменным :-) ).
Помните, что в Python есть 3 изменяемых встроенные: Набор
, список
, Словарь
, а все остальное - даже струны! - является неизменным.
Я согласен с волатильностью'ы комментарий. Но можно проверить следующим образом:
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
, пй-флаги
, святилище
):
class Default:
def __repr__( self ):
return "DEFAULT"
DEFAULT = Default()
...или эквивалент один-лайнер...:
DEFAULT = type( 'Default', (), { '__repr__': lambda x: 'DEFAULT' } )()
В отличие от по умолчанию = объект()
, это помогает проверка типов и предоставляет информацию, когда возникают ошибки-часто либо строковое представление (в"по умолчанию"
В) или имя класса ("по умолчанию"
в) используются в сообщениях об ошибках.
Вы можете проверить это с Фу.__по умолчанию__
и фу.__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
затем с помощью оператора =
и это
вы можете сравнить их
и для некоторых случаях код ниже, является достаточно
Например, вам нужно не менять значение по умолчанию, то можно проверить на равенство, а затем скопировать если так
def update_and_show(data=Example):
if data is Example:
data = copy.deepcopy(data)
update_inplace(data) #some operation
print(data)
@Ellioh'ы ответ работает в Python 2. В Python 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) с лучшей читабельности.
Немного странный подход:
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'ы suggestion выиграл'т быть автоматически документируются.