2011-01-10 41 views
7

Tôi đoán đó là cách chúng được gọi, nhưng tôi sẽ đưa ra các ví dụ chỉ trong trường hợp.Sự khác biệt giữa các lớp trang trí và chức năng trang trí

Decorator lớp:

class decorator(object): 
    def __init__(self, func): 
     self.func = func 

    def __call__(self, *args, **kwargs): 
     print 'something' 
     self.func(*args, **kwargs) 

Decorator chức năng:

def decorator(func): 
    def wrapper(*args, **kwargs): 
     print 'something' 
     return func(*args, **kwargs) 
    return wrapper 

Đang sử dụng một hay khác chỉ là vấn đề của hương vị? Có sự khác biệt thực tế nào không?

+3

Có sự khác biệt. Sự khác biệt là chính xác như nhau khi bạn muốn sử dụng một hàm thường xuyên so với một lớp thông thường – Falmarri

Trả lời

10

Nếu bạn có thể viết một hàm để thực hiện trang trí của mình, bạn nên sử dụng nó. Nhưng không phải tất cả các trang trí có thể dễ dàng được viết dưới dạng hàm - ví dụ: khi bạn muốn lưu trữ một số trạng thái nội bộ.

class counted(object): 
    """ counts how often a function is called """ 
    def __init__(self, func): 
     self.func = func 
     self.counter = 0 

    def __call__(self, *args, **kwargs): 
     self.counter += 1 
     return self.func(*args, **kwargs) 


@counted 
def something(): 
    pass 

something() 
print something.counter 

Tôi đã thấy mọi người (bao gồm cả bản thân mình) trải qua những nỗ lực vô lý để chỉ viết trang trí có chức năng. Tôi vẫn không biết tại sao, chi phí của một lớp thường hoàn toàn không đáng kể.

+4

Tất nhiên điều này * có thể * được viết dưới dạng hàm. Nhưng trong Python bạn thường thích một lớp trên một bao đóng để ẩn trạng thái. –

+1

@Sven: Bạn nói đúng, tôi đã thực sự phóng đại ở đó. –

+1

Tôi thực sự thích phiên bản Python (3.x) này (sử dụng niềng răng \ * sob \ * vì các chú thích ăn ngắt dòng): 'def counting (f) {x = 0; def wrapper (* args, ** kwargs) {nonlocal x; x + = 1; f (* args, ** kwargs)} return wrapper} '. Các phiên bản 2.x là đồng bằng xấu xí mặc dù (hack xung quanh thiếu 'nonlocal' với việc sử dụng một danh sách singleton và sử dụng nó là mục duy nhất). – delnan

3

Thông thường chỉ là vấn đề về hương vị. Hầu hết các chương trình Python đều sử dụng cách gõ vịt và không thực sự quan tâm liệu thứ mà chúng đang gọi là một hàm hay một thể hiện của một kiểu khác nào đó, miễn là nó có thể gọi được. Và bất cứ điều gì với phương thức __call__() đều có thể gọi được.

Có một vài lợi thế để sử dụng trang trí chức năng kiểu:

  • nhiều bụi khi trang trí của bạn không trả về một hàm wrapper (ví dụ, nó sẽ trả về các chức năng ban đầu sau khi làm điều gì đó với nó, chẳng hạn như thiết lập một thuộc tính).

  • Không cần lưu rõ ràng tham chiếu đến hàm ban đầu, vì điều này được thực hiện bằng cách đóng.

  • Hầu hết các công cụ giúp bạn tạo trang trí, chẳng hạn như functools.wraps() hoặc mô-đun bảo mật chữ ký số decorator của Michele Simionato, làm việc với trình trang trí theo chức năng.

  • Có thể có một số chương trình ở đâu đó không sử dụng gõ vịt, nhưng thực sự mong đợi một loại hàm, do đó, trả về hàm để thay thế một hàm về mặt lý thuyết là "an toàn hơn".

Vì những lý do này, tôi sử dụng trang trí theo phong cách chức năng hầu hết thời gian. Tuy nhiên, như một ví dụ, here là một trường hợp gần đây trong đó trình trang trí kiểu lớp tự nhiên hơn đối với tôi.

+2

'functools.wraps()' có thể được dùng để làm việc với các trình trang trí theo kiểu hàm, nhưng 'functools.update_wrapper (self, wrap)' trong '__init__' của một trình trang trí kiểu lớp hoạt động hoàn toàn tốt. –

Các vấn đề liên quan