2009-05-20 56 views
6

Tôi thích việc có thể để đo hiệu suất của các chức năng python tôi mã, do đó rất lâu tôi làm điều gì đó tương tự như sau ...Chức năng trang trí

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 

Vâng, tôi biết bạn có nghĩa vụ phải đo lường hiệu suất với timeit , nhưng điều này hoạt động tốt cho nhu cầu của tôi và cho phép tôi bật và tắt thông tin này để gỡ lỗi rất suôn sẻ.

Mã đó là từ trước khi tôi biết về trang trí chức năng ... Tôi không biết nhiều về chúng bây giờ, nhưng tôi nghĩ tôi có thể viết một trang trí làm như sau, sử dụng từ điển ** kwds:

some_function(arg1, arg2, ..., argN) # Does not time function 
some_function(arg1, arg2, ..., argN, verbose = True) # Times function 

tôi vẫn muốn lặp lại trong các hoạt động trước khi các chức năng của tôi, do đó làm việc sẽ là một cái gì đó giống như:

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 

tôi đoán điều này sẽ đòi hỏi sự trang trí để đếm số lượng các đối số, biết bao nhiêu chức năng ban đầu sẽ mất, p bất kỳ vượt quá, vượt qua số lượng phải của họ để chức năng ... Tôi không chắc chắn mặc dù về cách nói với python để làm điều này ... Có thể? Có cách nào tốt hơn để đạt được điều tương tự không?

Trả lời

9

Mặc dù inspect có thể giúp bạn có được một chút trên đường đi, những gì bạn muốn là nói chung không thể:

def f(*args): 
    pass 

Bây giờ có bao nhiêu lý lẽ không f mất? Vì *args**kwargs cho phép một số lượng đối số tùy ý, không có cách nào để xác định số đối số mà hàm cần có. Trong thực tế có những trường hợp mà hàm thực sự xử lý nhiều như có được ném vào nó!


Edit: nếu bạn sẵn sàng đưa lên với verbose như một đối số từ khóa đặc biệt, bạn có thể làm điều này:

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

(! Cảm ơn Alex Martelli cho kwargs.pop tip)

Tuy nhiên,
+0

nhưng những gì "some_function (arg1, arg2, ..., argN, True)" cũng phải chạy? –

+0

Vâng, điều đó đòi hỏi một loại trang trí khác. Một trong đó sử dụng kiểm tra hoặc có giá trị số bổ sung cho biết số lượng đối số cố định mà hàm được trang trí thực hiện. – Stephan202

+0

Cảm ơn bạn đã viết cho tôi Stephan! Tôi đã nghĩ về điều này trên đường về nhà từ công việc, và tôi đã đi đến kết luận rằng việc sử dụng các đối số từ khóa cho điều này là một cách tiếp cận tốt hơn, vì nó sẽ cho phép tôi bao gồm các tham số khác, chẳng hạn như bao nhiêu lần để thời gian hàm và liệu bản in có phản ánh thời gian trung bình, thời gian tối thiểu hoặc số liệu thống kê chi tiết hơn ... – Jaime

8

+1 về câu trả lời của Stephan202 (đưa câu trả lời này vào một câu trả lời riêng biệt vì các nhận xét không định dạng mã tốt!), Bit sau của mã trong câu trả lời đó:

verbose = False 
if 'verbose' in kwargs: 
    verbose = True 
    del kwargs['verbose'] 

thể được thể hiện rõ ràng hơn và ngắn gọn như sau:

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

+1. Không bao giờ sử dụng pop() trên một dict trước, tôi nghĩ. – Stephan202

0

nó có thể là khó khăn nhưng bạn có thể làm điều gì đó về những dòng này. Mã bên dưới cố gắng xóa mọi đối số thừa và in chúng ra.

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

ra là

func1 has extra args=['x'] 
func1 has extra args=['y'] 
Các vấn đề liên quan