Python: Comment vérifier si le paramètre optionnel de la fonction est défini

y a-t-il un moyen facile en Python de vérifier si la valeur d'un paramètre optionnel provient de sa valeur par défaut, ou parce que l'utilisateur l'a défini explicitement à l'appel de fonction?

46
demandé sur Matthias 2013-02-07 14:52:05

7 réponses

pas vraiment. La norme est d'utiliser une valeur par défaut que l'utilisateur ne devrait pas passer, par exemple, un object exemple:

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

habituellement, vous pouvez simplement utiliser None comme valeur par défaut, si cela n'a pas de sens comme valeur que l'utilisateur voudrait passer.

l'alternative est d'utiliser kwargs :

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

cependant ceci est trop verbeux et rend votre fonction plus difficile à l'utilisation comme documentation n'inclut pas automatiquement le paramètre param .

36
répondu ecatmur 2013-02-07 10:54:58

le décorateur de fonction suivant, explicit_checker , fait un ensemble de noms de paramètre de tous les paramètres donnés explicitement. Il ajoute le résultat comme paramètre supplémentaire ( explicit_params ) à la fonction. Il suffit de faire 'a' in explicit_params pour vérifier si le paramètre a est donné explicitement.

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
répondu Ellioh 2013-02-07 11:28:30

j'utilise parfois une chaîne unique universellement (comme un UUID).

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

de cette façon , Aucun utilisateur ne pourrait même deviner la valeur par défaut s'ils essayaient donc je peux être très sûr que lorsque je vois cette valeur pour arg , il n'a pas été passé.

5
répondu Jesse B Miller 2015-10-15 21:54:08

Je suis d'accord avec le commentaire de Volatility. Mais vous pouvez vérifier, de la manière suivante:

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
répondu isedev 2013-02-07 10:56:42

vous pouvez le vérifier à partir de foo.__defaults__ et foo.__kwdefaults__

voir un exemple simple ci-dessous

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

puis en utilisant l'opérateur = et is vous pouvez les comparer

et, dans certains cas, le code ci-dessous est utilisé

par exemple, vous devez éviter de changer la valeur par défaut alors vous pouvez vérifier l'égalité et puis faire face si oui

    def update_and_show(data=Example):
        if data is Example:
            data = copy.deepcopy(data)
        update_inplace(data) #some operation
        print(data)
1
répondu Alex 2018-03-31 16:08:04

Un peu fantasque approche serait:

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

qui produit:

passed default
z
passed different
b
not passed
z

maintenant ceci, comme je l'ai mentionné, est assez effrayant, mais il fait le travail. Toutefois, ceci est tout à fait illisible et de la même manière que ecatmur 's suggestion ne sera pas automatiquement documentée.

0
répondu dmg 2017-05-23 12:26:14

j'ai vu ce modèle à quelques reprises (par exemple bibliothèque unittest , py-flags , jinja ):

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

DEFAULT = Default()

...ou l'équivalent one-liner...:

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

contrairement à DEFAULT = object() , cela facilite la vérification de type et fournit de l'information lorsque des erreurs se produisent -- souvent, la représentation de chaîne ( "DEFAULT" ) ou le nom de classe ( "Default" ) sont utilisés dans les messages d'erreur.

0
répondu c z 2017-12-08 11:20:05