Питон: как проверить необязательный параметр функции

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

Комментарии к вопросу (8)

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

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

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

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

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

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

Комментарии (4)

Следующая функция-декоратор, 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)
Комментарии (3)

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

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

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

Комментарии (2)
Решение

Много ответов есть маленькие кусочки полная информация, поэтому я'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 ()
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 изменяемых встроенные: Набор, список, Словарь, а все остальное - даже струны! - является неизменным.

Комментарии (6)

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

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
Комментарии (3)

Я'вэ видел этот шаблон несколько раз (например, в библиотеке unittest, пй-флаги, святилище):

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

DEFAULT = Default()

...или эквивалент один-лайнер...:

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

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

Комментарии (0)

Вы можете проверить это с Фу.__по умолчанию__ и фу.__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)

@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) с лучшей читабельности.

Комментарии (0)

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

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

Теперь это, как я уже упоминал, довольно странно, но это делает работу. Однако это совершенно нечитабельна и аналогично ecatmursuggestion выиграл'т быть автоматически документируются.

Комментарии (2)