python - barplot - pandas plot
Python: cómo verificar si se establece un parámetro de función opcional (7)
¿Hay alguna manera fácil en Python para verificar si el valor de un parámetro opcional proviene de su valor predeterminado, o porque el usuario lo ha establecido explícitamente en la llamada a la función?
A veces uso una cadena universalmente única (como 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 esta forma, ningún usuario podría adivinar el valor predeterminado si lo intentaran, así que puedo estar muy seguro de que cuando vea ese valor para arg
, no se transfirió.
El siguiente decorador de funciones, explicit_checker
, crea un conjunto de nombres de parámetros de todos los parámetros explícitamente. Agrega el resultado como un parámetro adicional ( explicit_params
) a la función. Simplemente haz ''a'' in explicit_params
para verificar si el parámetro a
está dado explícitamente.
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)
Estoy de acuerdo con el comentario de Volatility. Pero puedes verificarlo de la siguiente manera:
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
He visto este patrón algunas veces (por ejemplo, library unittest
, py-flags
, jinja
):
class Default:
def __repr__( self ):
return "DEFAULT"
DEFAULT = Default()
... o el equivalente de un trazador de líneas ...:
DEFAULT = type( ''Default'', (), { ''__repr__'': lambda x: ''DEFAULT'' } )()
A diferencia de DEFAULT = object()
, esto ayuda a verificar el tipo y proporciona información cuando se producen errores. Con frecuencia, la representación de cadena ( "DEFAULT"
) o el nombre de clase ( "Default"
) se utilizan en los mensajes de error.
Puedes verificarlo desde foo.__defaults__
y foo.__kwdefaults__
ver un ejemplo simple abajo
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
luego usando operator =
y puedes compararlos
y para algunos casos el código de abajo es suficiente
Por ejemplo, debe evitar cambiar el valor predeterminado, entonces puede verificar la igualdad y luego hacer frente, si es así.
def update_and_show(data=Example):
if data is Example:
data = copy.deepcopy(data)
update_inplace(data) #some operation
print(data)
Realmente no. La forma estándar es usar un valor predeterminado que no se espera que el usuario pase, por ejemplo, una instancia de object
:
DEFAULT = object()
def foo(param=DEFAULT):
if param is DEFAULT:
...
Por lo general, puede usar None
como valor predeterminado, si no tiene sentido como valor que el usuario desea aprobar.
La alternativa es usar kwargs
:
def foo(**kwargs):
if ''param'' in kwargs:
param = kwargs[''param'']
else:
...
Sin embargo, esto es demasiado detallado y hace que su función sea más difícil de usar ya que su documentación no incluirá automáticamente el parámetro param
.
Un pequeño acercamiento anormal sería:
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()
Qué salidas:
passed default
z
passed different
b
not passed
z
Ahora bien, como mencioné, es bastante extraño, pero cumple su función. Sin embargo, esto es bastante ilegible y de forma similar a la suggestion no se documentará automáticamente.