105 votes

Comment vérifier si le paramètre de la fonction optionnelle est défini

Existe-t-il un moyen simple 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 la fonction ?

5voto

R. Yang Points 71

La réponse de @Ellioh fonctionne dans python 2. En python 3, le code suivant devrait fonctionner :

import inspect
from functools import wraps

def explicit_checker(f):
    varnames = inspect.getfullargspec(f)[0]
    @wraps(f)
    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

Cette méthode peut conserver les noms des arguments et les valeurs par défaut (au lieu de **kwargs) avec une meilleure lisibilité.

4voto

isedev Points 7410

Je suis d'accord avec le commentaire de Volatility. Mais vous pourriez 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

4voto

Alex Points 346

Vous pouvez le consulter à partir de foo.__defaults__ y 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 = y is vous pouvez les comparer

et pour certains cas, le code ci-dessous est suffisant

Par exemple, si vous devez éviter de modifier la valeur par défaut, vous pouvez vérifier l'égalité et, le cas échéant, la copier.

    def update_and_show(data=Example):
        if data is Example:
            data = copy.deepcopy(data)
        update_inplace(data) #some operation
        print(data)

Il est également très pratique à utiliser getcallargs de inspect car il renvoie des arguments réels avec lesquels la fonction sera invoquée. Vous passez une fonction et des args et kwargs à celle-ci ( inspect.getcallargs(func, /, *args, **kwds) ), il retournera les arguments réels de la méthode utilisés pour l'invocation, en tenant compte des valeurs par défaut et d'autres éléments. Regardez l'exemple ci-dessous.

from inspect import getcallargs

# we have a function with such signature
def show_params(first, second, third=3):
    pass

# if you wanted to invoke it with such params (you could get them from a decorator as example)
args = [1, 2, 5]
kwargs = {}
print(getcallargs(show_params, *args, **kwargs))
#{'first': 1, 'second': 2, 'third': 5}

# here we didn't specify value for d
args = [1, 2, 3, 4]
kwargs = {}

# ----------------------------------------------------------
# but d has default value =7
def show_params1(first, *second, d = 7):
    pass

print(getcallargs(show_params1, *args, **kwargs))
# it will consider b to be equal to default value 7 as it is in real method invocation
# {'first': 1, 'second': (2, 3, 4), 'd': 7}

# ----------------------------------------------------------
args = [1]
kwargs = {"d": 4}

def show_params2(first, d=3):
    pass

print(getcallargs(show_params2, *args, **kwargs))
#{'first': 1, 'd': 4}

https://docs.python.org/3/library/inspect.html

2voto

lazieburd Points 183

C'est une variante de la réponse de Stefano, mais je la trouve un peu plus lisible :

not_specified = {}

def foo(x=not_specified):
    if x is not_specified:
            print("not specified")
    else:
            print("specified")

1voto

dmg Points 6493

Une approche un peu bizarre 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()

Quelles sorties :

passed default
z
passed different
b
not passed
z

Comme je l'ai dit, c'est assez bizarre, mais ça fait l'affaire. Cependant, c'est assez illisible et, comme dans le cas de ecatmur 's suggestion ne seront pas automatiquement documentés.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X