2009-05-20 29 views
6

Python fonksiyonları çok kodun performansını ölçmek mümkün olmak gibi, bu yüzden çok sık ben buna benzer bir şey yapmak ...Fonksiyon Dekoratörler

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 

Evet, sen sürümüyle gelen timeit ile performansını ölçmek gerekiyor biliyorum ama bu sadece ihtiyaçlar için iyi çalışıyor ve bu bilgileri çok kolay bir şekilde hata ayıklamak için açıp kapatmamı sağlıyor. Tabii ki bu kod ben işlev dekoratörler hakkında tanımadan önce oldu

... Şimdi onlar hakkında çok şey biliyorum, ama ben ** kwds sözlüğünü kullanarak aşağıdakileri yaptığı bir dekoratör yazabilirsiniz düşünüyorum Değil:

some_function(arg1, arg2, ..., argN) # Does not time function 
some_function(arg1, arg2, ..., argN, verbose = True) # Times function 
Ben yine de bir şey fazla gibi çalışma olacağını, böylece benim fonksiyonları bilinen çalışma çoğaltmak istiyoruz

: Bu argüman sayısını saymak için dekoratör gerektirecektir tahmin

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 

, biliyorum orijinal fonksiyon kaç tane alacak p herhangi bir fazlalık, doğru sayıyı işleve iletin ... Ben python'a bunu nasıl söyleyeceğimi bilmiyorum ... Bu mümkün mü? Aynı şeyi elde etmenin daha iyi bir yolu var mı? Ne istediğinizi size yolda biraz alabilirsiniz inspect olsa

cevap

9

, genel olarak değil mümkündür:

def f(*args): 
    pass 

Şimdi kaç argüman alır f geliyor? *args ve **kwargs, isteğe bağlı sayıda argümana izin verdiğinden, bir fonksiyonun gerektirdiği argüman sayısını belirlemenin bir yolu yoktur. Aslında, işlevin gerçekten atıldığı kadar çok işlediği durumlar var!


Düzenleme: Eğer bir özel anahtar kelime argüman olarak verbose katlanmak çekinmiyorsanız, bunu yapabilirsiniz:

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

(! kwargs.pop Tavsiye için teşekkürler Alex Martelli)

+0

ama ne "some_function (arg1, arg2, ..., argN, True)" da çalıştırmalısınız nedir? –

+0

Bu başka bir dekoratör gerektiriyor. Biri, incelenen fonksiyonun aldığı sabit sayıda argümanı gösteren ek bir sayısal değeri inceler veya kullanır. – Stephan202

+0

Benim için yazdığınız için teşekkürler Stephan! İşten eve dönerken bu konuyu düşünüyordum ve bunun için anahtar kelime argümanlarını kullanmanın daha iyi bir yaklaşım olduğu sonucuna vardım. ve çıktının ortalama zamanı, minimum zamanı veya daha ayrıntılı istatistikleri yansıtıp yansıtmadığı ... – Jaime

8

+1 Stephan202'nin cevabı, ancak (yorumlar, kodları iyi biçimlendirmediği için ayrı bir cevapta koyar!), Aşağıdaki koddaki bu bit kodu:

verbose = False 
if 'verbose' in kwargs: 
    verbose = True 
    del kwargs['verbose'] 
çok daha açık ve kesin bir şekilde ifade edilebilir:

verbose = kwargs.pop('verbose', False) 
+0

+1. Daha önce hiç bir dict üzerinde pop() kullanmadım, bence. – Stephan202

0

zor olabilir ama bu hatlarda bir şeyler yapabiliriz. Aşağıdaki kod herhangi bir ek argümanı kaldırmaya ve yazdırmaya çalışır.

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

çıkış

func1 has extra args=['x'] 
func1 has extra args=['y']