2013-02-07 8 views
42

Czy istnieje prosty sposób w Pythonie, aby sprawdzić, czy wartość opcjonalnego parametru pochodzi od jego wartości domyślnej, czy też użytkownik ustawił go jawnie przy wywołaniu funkcji?Python: jak sprawdzić, czy opcjonalny parametr funkcji jest ustawiony

+2

Dlaczego to ma znaczenie? – Volatility

+9

Bo chcę to sprawdzić w tej funkcji oczywiście :) – Matthias

+0

Po prostu użyj 'None' jako domyślnego i sprawdź to.Jeśli naprawdę możesz skonfigurować ten test, możesz również wykluczyć jakąkolwiek możliwość, aby użytkownik jawnie przekazał wartość, która wywołuje domyślne zachowanie. –

Odpowiedz

32

Niezupełnie. Standardowym sposobem jest użycie wartości domyślnej, której użytkownik nie powinien przejść, np. object instancja:

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

Zazwyczaj można po prostu użyć None jako wartość domyślną, jeśli to nie ma sensu, jako wartość użytkownik chciałby przekazać.

Alternatywą jest użycie kwargs:

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

Jednak jest zbyt rozwlekły i sprawia, że ​​funkcja trudniejsze do wykorzystania jako jego dokumentacja nie będzie automatycznie zawierać parametr param.

+0

dziękuję, myślę, że będę trzymać się czeku na Brak – Matthias

+4

Widziałem również kilka osób korzysta z wbudowanego Ellipsis dla miejsc, gdzie jest to potrzebne i Brak jest uważane za prawidłowe wejście. Jest to zasadniczo takie samo jak w pierwszym przykładzie. – GrandOpener

2

Zgadzam się z komentarzem Wrażliwości. Ale można sprawdzić w następujący sposób:

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 
+0

Wierzę, że opcjonalny parametr jest coś jak "def func (opcjonalne = wartość)' nie '** kwargs' –

+0

To jest coś, co jest nieco otwarte na interpretację. Jaka jest rzeczywista różnica między argumentem a wartością domyślną a argumentem słowa kluczowego? Oba wyrażane są przy użyciu tej samej składni "keyword = value". – isedev

+0

Nie zgadzam się, ponieważ cel opcjonalnych parametrów i '** kwargs' jest nieco inny. P.S. nie ma problemu około -1 :) I moje -1 dla ciebie było przypadkowe :) –

0

Trochę dziwaczne podejście byłoby:

które wyjścia:

passed default 
z 
passed different 
b 
not passed 
z 

Teraz, jak już wspomniano, jest dość kapryśny , ale spełnia swoją funkcję. Jest to jednak całkowicie nieczytelne i podobnie do ecatmur 's suggestion nie będzie automatycznie dokumentowane.

+1

Możesz chcieć dołączyć zachowanie 'check_f ('z')', które jest również, jak mówisz, dziwaczne. –

+0

@ MichaelJ.Barber Dobra uwaga. Będziesz musiał zrobić "magię" również z * args. Chodzi mi jednak o to, że jest to możliwe, ale potrzebuję teraz, czy wartość domyślna jest przekazywana, czy nie, jest złym projektem. – dmg

7

Następujący dekorator funkcji, explicit_checker, tworzy zestaw nazw parametrów wszystkich podanych parametrów jawnie. Dodaje wynik jako dodatkowy parametr (explicit_params) do funkcji. Po prostu wykonaj 'a' in explicit_params, aby sprawdzić, czy parametr a jest jawnie podany.

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

Czasami używam uniwersalnego unikalnego ciągu (jak UUID).

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

W ten sposób użytkownik może nawet nie domyślić domyślny gdyby próbowali więc mogę być pewni, że gdy widzę, że wartość dla arg, nie została podjęta w.

+0

To trochę hack, ale zwięzłe i skuteczne ... – F1Rumors

+0

Obiekty Pythona są referencjami, możesz po prostu użyć 'object()' zamiast 'uuid4()' - to wciąż wyjątkowa _instance_, która jest tym, czym jest 'is' czeki –

0

Widziałem ten wzór kilka razy (na przykład biblioteka unittest, py-flags, jinja):

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

DEFAULT = Default() 

... lub równowartość jednego-liner ...:

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

przeciwieństwie DEFAULT = object(), to pomaga typu sprawdzanie i dostarcza informacji w przypadku wystąpienia błędów - często albo reprezentacja string ("DEFAULT") lub nazwa klasy ("Default") stosuje się w komunikaty o błędach.