2009-03-20 33 views
34

Tôi muốn xây dựng các lớp học để sử dụng như trang trí với các nguyên tắc sau còn nguyên vẹn:lớp Decorator bằng Python

  1. Nó nên có thể để ngăn xếp nhiều trang trí lớp học như vậy trên đầu ra 1 chức năng.
  2. Con trỏ tên hàm kết quả không thể phân biệt được với cùng chức năng mà không có trang trí, lưu có thể chỉ với kiểu/lớp đó.
  3. Đặt hàng ra khỏi trang trí không nên có liên quan trừ khi thực sự bắt buộc bởi người trang trí. I E. trang trí độc lập có thể được áp dụng theo bất kỳ thứ tự nào.

này là dành cho một dự án Django, và các trường hợp cụ thể tôi đang làm việc trên hiện nay phương pháp này cần 2 trang trí, và xuất hiện như một hàm trăn bình thường:

@AccessCheck 
@AutoTemplate 
def view(request, item_id) {} 

@AutoTemplate thay đổi chức năng để thay vì trả lại một HttpResponse, nó chỉ trả về một từ điển để sử dụng trong ngữ cảnh. Một RequestContext được sử dụng, và tên mẫu được suy ra từ tên phương thức và mô-đun.

@AccessCheck thêm séc bổ sung cho người dùng dựa trên item_id.

Tôi đoán nó chỉ là để có được các nhà xây dựng quyền và sao chép các thuộc tính thích hợp, nhưng thuộc tính nào là những?

Các trang trí sau đây sẽ không làm việc như tôi mô tả:

class NullDecl (object): 
    def __init__ (self, func): 
     self.func = func 
    def __call__ (self, * args): 
     return self.func (*args) 

Như thể hiện bằng đoạn mã sau:

@NullDecl 
@NullDecl 
def decorated(): 
    pass 

def pure(): 
    pass 

# results in set(['func_closure', 'func_dict', '__get__', 'func_name', 
# 'func_defaults', '__name__', 'func_code', 'func_doc', 'func_globals']) 
print set(dir(pure)) - set(dir(decorated)); 

Thêm vào đó, hãy thử và thêm "in func tên." Trong NullDecl constructor, và nó sẽ làm việc cho trang trí đầu tiên, nhưng không phải là thứ hai - như tên sẽ bị thiếu.

Refined eduffy 's trả lời một chút, và có vẻ như làm việc khá tốt:

class NullDecl (object): 
    def __init__ (self, func): 
     self.func = func 
     for n in set(dir(func)) - set(dir(self)): 
      setattr(self, n, getattr(func, n)) 

    def __call__ (self, * args): 
     return self.func (*args) 
    def __repr__(self): 
     return self.func 
+1

+1 vì tôi m không chắc chắn lý do tại sao một người nào đó đã bỏ phiếu cho câu hỏi này. Đó là một câu hỏi hay: hầu hết các câu hỏi trang trí trên SO không sử dụng các lớp làm trang trí. Bất cứ ai có thể nghĩ đến việc sử dụng một lớp thay vì một hàm, đó là một câu hỏi hợp lệ và có thể hoàn toàn phù hợp với trường hợp sử dụng. –

Trả lời

21

Một làm-gì lớp trang trí sẽ trông như thế này:

class NullDecl (object): 
    def __init__ (self, func): 
     self.func = func 
     for name in set(dir(func)) - set(dir(self)): 
     setattr(self, name, getattr(func, name)) 

    def __call__ (self, *args): 
     return self.func (*args) 

Và sau đó bạn có thể áp dụng thông thường:

@NullDecl 
def myFunc (x,y,z): 
    return (x+y)/z 
+0

Kiểm tra nhận xét của tôi về lý do mã của bạn không hoạt động như tôi đã nêu. – Staale

+0

Tôi đã thêm bản sửa lỗi của riêng mình để nó hoạt động bình thường. Cảm ơn sự giúp đỡ, thường giúp khởi đầu rất cơ bản. – Staale

+6

Có lẽ cũng có một nơi để thêm '** kwargs' vào định nghĩa' def __call__ (self, * args): '(và để gọi' self.func' sau này)? –

7

Để tạo một trình trang trí kết thúc tốt đẹp các chức năng trong một vấn đề khiến chúng không thể phân biệt được với hàm ban đầu, hãy sử dụng functools.wraps.

Ví dụ:


def mydecorator(func): 
    @functools.wraps(func): 
    def _mydecorator(*args, **kwargs): 
     do_something() 
     try: 
      return func(*args, **kwargs) 
     finally: 
      clean_up() 
    return _mydecorator 

# ... and with parameters 
def mydecorator(param1, param2): 
    def _mydecorator(func): 
     @functools.wraps(func) 
     def __mydecorator(*args, **kwargs): 
      do_something(param1, param2) 
      try: 
       return func(*args, **kwargs) 
      finally: 
       clean_up() 
     return __mydecorator 
    return _mydecorator 

(sở thích cá nhân của tôi là tạo ra trang trí sử dụng các chức năng, không phải lớp)

Trật tự của trang trí như sau:


@d1 
@d2 
def func(): 
    pass 

# is equivalent to 
def func(): 
    pass 

func = d1(d2(func)) 
+0

Tôi thích sử dụng các lớp học, nhưng tôi đoán đó là do nền Java của tôi. Tôi nghĩ rằng tôi đọc một người nào đó đề nghị sử dụng các lớp học cho trang trí, nhưng tôi đoán đó là lời khuyên xấu. – Staale

+2

Nếu bạn muốn sử dụng các lớp học, tôi không chắc chắn nơi bạn đặt @ functools.wraps. Tôi cảm thấy các chức năng như người trang trí có nhiều chất pythonic hơn. Và mã ngắn hơn. – codeape

+0

Trong ví dụ tham số của bạn, "func" xuất phát từ đâu? Nó không có trong danh sách các tham số cho mydecorator() ... –

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