Décorateurs Fonction
-
23-08-2019 - |
Question
J'aime pouvoir mesurer les performances des fonctions de python I de code, donc très souvent je fais quelque chose de similaire à ce ...
import time
def some_function(arg1, arg2, ..., argN, verbose = True) :
t = time.clock() # works best in Windows
# t = time.time() # apparently works better in Linux
# Function code goes here
t = time.clock() - t
if verbose :
print "some_function executed in",t,"sec."
return return_val
Oui, je sais que vous êtes censé mesurer la performance avec timeit, mais cela fonctionne très bien pour mes besoins, et me permet de transformer ces informations et de désactiver le débogage très bien.
Ce code de cours a été d'avant, je connaissais les décorateurs de fonction ... Pas que je sache beaucoup sur eux, mais je pense que je pourrais écrire un décorateur qui a fait ce qui suit, en utilisant le ** dictionnaire de kwds:
some_function(arg1, arg2, ..., argN) # Does not time function
some_function(arg1, arg2, ..., argN, verbose = True) # Times function
Je voudrais néanmoins dupliquer le travail avant de mes fonctions, de sorte que le travail serait quelque chose comme:
some_function(arg1, arg2, ..., argN) # Does not time function
some_function(arg1, arg2, ..., argN, False) # Does not time function
some_function(arg1, arg2, ..., argN, True) # Times function
Je suppose que cela nécessiterait le décorateur de compter le nombre d'arguments, savent combien la fonction d'origine prendra, la bande tout en excès, passer le bon nombre d'entre eux à la fonction ... Je suis certain que sur la façon dont dire python faire ... Est-il possible? Y at-il une meilleure façon d'atteindre le même?
La solution
Bien que inspecter peut vous être un peu sur le chemin, ce que vous veulent est en général pas possible:
def f(*args):
pass
Maintenant, combien d'arguments ne f
prendre? Depuis *args
et **kwargs
permettent un nombre arbitraire d'arguments, il n'y a aucun moyen de déterminer le nombre d'arguments d'une fonction nécessite. En fait, il y a des cas où la fonction poignées vraiment autant qu'il y sont jetés à elle!
Modifier si vous êtes prêt à mettre en place avec verbose
comme argument de mot-clé spécial, vous pouvez faire ceci:
import time
def timed(f):
def dec(*args, **kwargs):
verbose = kwargs.pop('verbose', False)
t = time.clock()
ret = f(*args, **kwargs)
if verbose:
print("%s executed in %ds" % (f.__name__, time.clock() - t))
return ret
return dec
@timed
def add(a, b):
return a + b
print(add(2, 2, verbose=True))
(Merci Alex Martelli pour la pointe de kwargs.pop
!)
Autres conseils
1 sur la réponse de Stephan202, cependant (mettre cela dans une réponse distincte étant donné que les commentaires ne formatez pas bien le code!), Le bit suivant du code dans cette réponse:
verbose = False
if 'verbose' in kwargs:
verbose = True
del kwargs['verbose']
peut être exprimé plus clairement et de façon concise comme:
verbose = kwargs.pop('verbose', False)
il peut être difficile, mais vous pouvez faire quelque chose sur ces lignes. Code ci-dessous tente de supprimer des arguments supplémentaires et les imprime.
def mydeco(func):
def wrap(*args, **kwargs):
"""
we want to eat any extra argument, so just count args and kwargs
and if more(>func.func_code.co_argcount) first take it out from kwargs
based on func.func_code.co_varnames, else last one from args
"""
extraArgs = []
newKwargs = {}
for name, value in kwargs.iteritems():
if name in func.func_code.co_varnames:
newKwargs[name] = value
else:
extraArgs.append(kwargs[name])
diff = len(args) + len(newKwargs) - func.func_code.co_argcount
if diff:
extraArgs.extend(args[-diff:])
args = args[:-diff]
func(*args, **newKwargs)
print "%s has extra args=%s"%(func.func_name, extraArgs)
return wrap
@mydeco
def func1(a, b, c=3):
pass
func1(1,b=2,c=3, d="x")
func1(1,2,3,"y")
sortie est
func1 has extra args=['x']
func1 has extra args=['y']