Питон: как проверить, установлен ли дополнительный параметр функции

Есть ли легкий способ в Пайтоне проверить, поступает ли ценность дополнительного параметра из его значения по умолчанию, или потому что пользователь установил его явно при вызове функции?

45
Это может быть сделано намного большим количеством и красивого способа многократного использования, чем в ответе, который вы приняли, по крайней мере для CPython. См. мой ответ ниже.
добавлено автор Ellioh, источник
Просто используйте не Ни один как дефолт и чек на это. Если вы действительно могли бы настроить этот тест, you' d также исключают любую возможность для пользователя явно передать стоимость, которая призывает поведение по умолчанию.
добавлено автор Michael J. Barber, источник
@Volatility: имеет значение, если у вас есть два набора дефолтов. Рассмотрите рекурсивный класс: Классифицирует Мой (): определение __ init __ (сам, _p=None, a=True, b=True, c=False) Пользователь называет его с x=My (b=False) . Метод класса мог назвать себя с x=My (_p=self, c=True) , если функции могли бы обнаружить, что b явно не установлен и что переменные сброса должны быть переданы от верхнего уровня. Но если они can' t, рекурсивные вызовы должны передать каждую переменную явно: x=My (a=self.a, b=self.b, c=True, d=self.d...) .
добавлено автор Dave, источник
Ну, просто найденный dict.pop (ключ [дефолт]) довольно странный: "Если , который дефолт не дан и ключ , не находится в словаре, , KeyError поднят". И это приводит меня к этому вопросу.
добавлено автор neuront, источник
Почему это имело бы значение?
добавлено автор Volatility, источник
@Dave, но является этим, о чем вопрос? В моем понимании вопрос спрашивает, как дифференцироваться x=My() и x=My (a=True) . Ваш сценарий включает назначающие дополнительные параметры стоимость кроме их значения по умолчанию.
добавлено автор Volatility, источник
Поскольку я хочу проверить его в ту функцию, конечно:)
добавлено автор Matthias, источник

7 ответы

Едва ли. Стандартный путь состоит в том, чтобы использовать значение по умолчанию, которое пользователь, как ожидали бы, не передаст, например, объект случай:

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

Обычно можно просто использовать не Ни один как значение по умолчанию, если бы это не имеет смысла как стоимости, пользователь хотел бы пройти.

Альтернатива должна использовать kwargs :

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

Однако, это чрезмерно многословно и делает вашу функцию более трудной использовать, поскольку ее документация не будет автоматически включать param параметр.

35
добавлено
спасибо, я думаю, что буду придерживаться проверки ни на Один
добавлено автор Matthias, источник
I' ve, также замеченный несколько человек использовать Эллипсис, встроенный для мест, где это необходимо и Ни один не считают действительным входом. Это - по существу то же самое как первый пример.
добавлено автор GrandOpener, источник

Следующий декоратор функции, 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)
8
добавлено

Я иногда использую универсально уникальную строку (как UUID).

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

Таким образом, никакой пользователь не мог даже предположить дефолт, если бы они попробовали так, я могу быть очень уверен что, когда я вижу, что стоимость для аргумент , он не был передан в.

5
добавлено
Объекты питона - ссылки, можно просто использовать объект() вместо uuid4() - it' s все еще уникальный случай , который является тем, какой проверки
добавлено автор c z, источник
It' s что-то вроде хакерского проникновения, но сжатый и эффективный...
добавлено автор F1Rumors, источник

Я соглашаюсь с комментарием Изменчивости. Но вы могли проверить следующим образом:

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
2
добавлено
That' s что-то, что несколько открыто для интерпретации. What' s фактическое различие между спором со значением по умолчанию и аргументом ключевого слова? Они оба выражаются, используя тот же самый синтаксис "keyword=value".
добавлено автор isedev, источник
Я полагаю, что дополнительный параметр - что-то как определение func (optional=value) не ** kwargs
добавлено автор Zaur Nasibov, источник
Я не соглашаюсь, потому что цель дополнительных параметров и ** kwargs немного отличается. P.S. никакой problem' s приблизительно-1:) И мои-1 для вас было случайно:)
добавлено автор Zaur Nasibov, источник

Немного странного подхода было бы:

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 предложение не будет автоматически зарегистрирован.

0
добавлено
Положительная сторона @MichaelJ.Barber. You' ll должны сделать некоторое "волшебство" с *args также. Однако мой пункт был то, что это возможно, но нуждающийся к теперь, передается ли значение по умолчанию или не является плохим дизайном.
добавлено автор dmg, источник
Вы могли бы хотеть включать поведение check_f (' z') , который является также, как вы говорите, странный.
добавлено автор Michael J. Barber, источник

Я видел этот образец несколько раз (например, библиотека unittest , py-флаги , Джинджа ):

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

DEFAULT = Default()

... или эквивалентная острота...:

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

В отличие от ДЕФОЛТ = объект() , это помогает проверке типа и предоставляет информацию, когда ошибки происходят - часто любой, который строковое представление ( "ДЕФОЛТ" ) или имя класса ( "Дефолт" ) используется в сообщениях об ошибках.

0
добавлено

You can check it from foo.__defaults__ and 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

тогда при помощи оператора = и , можно сравнить их

и поскольку некоторый кодовый рев случаев достаточно

Например, необходимо постараться не изменять значение по умолчанию тогда, можно проверить равенство и затем справиться раз так

    def update_and_show(data=Example):
        if data is Example:
            data = copy.deepcopy(data)
        update_inplace(data) #some operation
        print(data)
0
добавлено
Python
Python
7 654 участник(ов)

Уютный чат для профессионалов, занимающихся поиском питоньих мудростей. Как не получить бан: https://t.me/ru_python/577926

Python beginners
Python beginners
4 449 участник(ов)

Вопросы про Python для чайников. Cпам и троллинг неприемлем. Не злоупотребляйте стикерами. Частозадаваемые вопросы: https://github.com/ru-python-beginners/faq/blob/master/README.md Статистика тут: https://grstats.me/chat/x4qym2k5uvfkr3al6at7

pro.python
pro.python
1 090 участник(ов)

Сообщество разработчиков под Python Создатель: @rodgelius

Rude Python
Rude Python
971 участник(ов)

Python без „девочек”, здесь матерятся и унижают Django. Not gay friendly. Правила: t.me/rudepython/114107 @rudepython | t.me/rudepython

rupython
rupython
509 участник(ов)

Группа создана с целью оперативного получения ответов на возникающие вопросы по разработке на яп python, смежные темы, а также человеческого общения. Приветствую!

Python-programming
Python-programming
266 участник(ов)

Чат группы вконтакте https://vk.com/python_community