2012-02-15 34 views
5

Chỉ cần đưa ra ý kiến ​​về việc liệu điều sau đây là hợp lý hay có cách tiếp cận tốt hơn. Về cơ bản tôi muốn một trang trí sẽ áp dụng cho một hàm hoặc một lớp thực hiện __call__.Trang trí python cho lớp HOẶC chức năng

Bạn chỉ có thể có trang trí thông thường và trang trí __call__ một cách rõ ràng nhưng sau đó trang trí được giấu bên trong định nghĩa lớp và ít rõ ràng hơn. Có lẽ tôi đang thiếu một giải pháp đơn giản hơn.

import types 
from functools import wraps 

class dec: 
    """ Decorates either a class that implements __call__ 
     or a function directly. 
    """ 
    def __init__(self, foo): 
     self._foo = foo 

    def __call__(self, target): 
     wraps_class = isinstance(target, types.ClassType) 
     if wraps_class: 
      fun = target.__call__ 
     else: 
      fun = target 

     @wraps(fun) 
     def bar(*args, **kwds): 
      val = args[1] if wraps_class else args[0] 
      print self._foo, val 
      return fun(*args, **kwds) 
     if wraps_class: 
      target.__call__ = bar 
      return target 
     else: 
      return bar 

@dec('A') 
class a: 
    # you could decorate here, but it seems a bit hidden 
    def __call__(self, val): 
     print "passed to a:", val 

@dec('B') 
def b(val): 
    print "passed to b:", val 

a()(11) 
b(22) 

Trả lời

4

Cá nhân, tôi sẽ chia thành hai trang trí: một luôn luôn kết thúc tốt đẹp một hàm:

def func_dec(foo, is_method=False): 
    def wrapper(fun): 
     @wraps(fun) 
     def bar(*args, **kwds): 
      val = args[1] if is_method else args[0] 
      print foo, val 
      return fun(*args, **kwds) 
     return bar 
    return wrapper 

Và một phát hiện nếu nó nên sửa đổi một phương pháp __call__ hoặc đơn giản là quấn một hàm:

def dec(foo): 
    def wrapper(obj): 
     if inspect.isclass(obj): 
      obj.__call__ = func_dec(foo, is_method=True)(obj.__call__) 
      return obj 
     else: 
      return func_dec(foo)(obj) 
    return wrapper 

Lưu ý rằng inspect.isclass sẽ hoạt động chính xác với cả các kiểu kiểu cũ và kiểu mới.

1

Đó là một ý tưởng khá khéo léo. Nó có vẻ tốt với tôi, mặc dù nó có thể được thêm pythonic để trang trí __call__ trực tiếp kể từ khi "rõ ràng là tốt hơn so với tiềm ẩn". Có một chút chi phí khái niệm để có một trang trí làm hai việc.

(Tôi tự hỏi nếu nó sẽ tồi tệ hơn hay tốt hơn để làm cho một trang trí mà mất bất kỳ chức năng trang trí và biến nó thành một trang trí chức năng kép/lớp ...)

3

tôi không thực sự thích cách tiếp cận của bạn. Phương pháp __call__() được sử dụng nếu một cá thể được gọi. Thay vào đó, việc tự gọi lớp sẽ gọi số __init__(), vì vậy tôi không thấy điều này thực sự giống nhau.

Trang trí của bạn sẽ không hoạt động đối với các lớp kiểu mới (trực tiếp hoặc gián tiếp có nguồn gốc từ object). Làm cho bạn một ưu và chỉ cần trang trí __call__() nếu đây là những gì bạn muốn. Hoặc viết một chức năng của nhà sản xuất tạo ra và trang trí trường hợp của lớp - điều này tương tự với cách trang trí một hàm, vì cá thể có thể gọi trực tiếp và bạn không phải lộn xộn với tham số self.

+0

Cảm ơn. (Và nếu có ai quan tâm, DzinX lưu ý sửa lỗi để làm cho nó hoạt động với các lớp kiểu mới là kiểm tra với inspect.isclass.) –

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