2012-05-16 23 views
9

Thư viện chuẩn Python có lối tắt để viết trang trí chấp nhận đối số không?Python: phím tắt để viết trang trí chấp nhận đối số?

Ví dụ, nếu tôi muốn viết một trang trí như with_timeout(timeout):

@with_timeout(10.0) 
def cook_eggs(eggs): 
    while not eggs.are_done(): 
     eggs.cook() 

tôi phải viết một cái gì đó như:

def with_timeout(timeout): 
    _func = [None] 
    def with_timeout_helper(*args, **kwargs): 
     with Timeout(timeout): 
      return _func[0](*args, **kwargs) 
    def with_timeout_return(f): 
     return functools.wraps(f)(with_timeout_helper) 
    return with_timeout_return 

Nhưng đó là hết sức tiết. Có một phím tắt khiến cho những người trang trí chấp nhận các đối số dễ viết hơn không?

Lưu ý: Tôi nhận ra rằng nó cũng có thể sử dụng ba hàm lồng nhau để triển khai trình trang trí với đối số… Nhưng điều đó cũng chỉ có một chút tối ưu.

Ví dụ, có thể là một cái gì đó giống như một chức năng @decorator_with_arguments:

@decorator_with_arguments 
def timeout(f, timeout): 
    @functools.wraps(f) 
    def timeout_helper(*args, **kwargs): 
     with Timeout(timeout): 
      return f(*args, **kwargs) 
    return timeout_helper 
+0

Nếu bạn cần thêm trợ giúp với trang trí và chú thích xem bài đăng blog của tôi ở đây. http://blog.mattalcock.com/2013/1/5/decorates-and-annotations/ –

Trả lời

7

tôi có xu hướng để viết trang trí của tôi như các lớp học phải trung thực

class TestWithArgs(object): 
    def __init__(self, *deco_args, **deco_kwargs): 
     self.deco_args = deco_args 
     self.deco_kwargs = deco_kwargs 
    def __call__(self, func): 
     def _wrap(self, *args, **kwargs): 
      print "Blah blah blah" 
      return func(*args, **kwargs) 
     return _wrap 

của nó không có gì nếu không muốn nói một chút rõ ràng hơn

4

tôi biết bạn nói nó cảm thấy suboptimal nhưng tôi vẫn cảm thấy rằng bằng cách sử dụng ba mô hình lồng nhau là giải pháp sạch nhất. Hai hàm bên trong chỉ là cách 'bình thường' của việc định nghĩa một trình trang trí cho một hàm lấy đối số (xem example trong các tài liệu của python cho @wraps). Cái bên ngoài thực sự chỉ là một hàm nhận và đối số và trả về một trang trí.

def with_timeout(timeout): 
    def decorator(f): 
     @wraps(f) 
     def wrapper(*args, **kwargs): 
      with Timeout(timeout): 
       return f(*args, **kwargs) 
     return wrapper 
    return decorator 
+0

Xin lỗi, nhưng tôi không đồng ý. Không có gì ở tất cả sạch về ba hàm lồng nhau (hoặc, cho rằng vấn đề, cách tôi làm điều đó). Đó là một hack. Tốt hơn là, ví dụ, một trang trí '@ decorator_with_arguments' (xem câu hỏi được cập nhật của tôi). –

4

Dựa trên đề nghị Jakob, tôi đã thực hiện một Decorator lớp học nhỏ, mà tôi cảm thấy không một công việc khá phong nha:

class Decorator(object): 
    def __call__(self, f): 
     self.f = f 
     return functools.wraps(f)(lambda *a, **kw: self.wrap(*a, **kw)) 

    def wrap(self, *args, **kwrags): 
     raise NotImplemented("Subclasses of Decorator must implement 'wrap'") 

class with_timeout(Decorator): 
    def __init__(self, timeout): 
     self.timeout = timeout 

    def wrap(self, *args, **kwargs): 
     with Timeout(timeout): 
      return self.f(*args, **kwargs) 
+1

Tôi đã sửa lỗi đánh máy trên phương thức 'with_timeout'' warp' –

+0

D'oh! Cảm ơn. Đó có lẽ là lần thứ 8 tôi gõ 'warp' thay vì 'wrap' tối nay. Và tôi đã không gõ 'quấn' nhiều lần. –

0

Đầu tiên, chúng ta có thể xác định một chút meta-trang trí:

def decorator_with_arguments(wrapper): 
    return lambda *args, **kwargs: lambda func: wrapper(func, *args, **kwargs) 

Điều đó cho phép chúng tôi tạo ra trang trí chấp nhận lập luận như vậy:

@decorator_with_arguments 
def my_wrapper(func, *decorator_args, **decorator_kwargs): 
    def wrapped(*call_args, **call_kwargs): 
     print "from decorator:", decorator_args, decorator_kwargs 
     func(*call_args, **call_kwargs) 
    return wrapped 

Mà sau đó có thể được sử dụng bình thường:

@my_wrapper(1, 2, 3) 
def test(*args, **kwargs): 
    print "passed directly:", args, kwargs 

test(4, 5, 6) 

Thêm functools.wraps trang trí là trái như một bài tập :)

0

mất khác, mà không sử dụng lambdas:

def decorator_with_arguments(f): 
    @functools.wraps(f) 
    def with_arguments_helper(*args, **kwargs): 
     def decorator(g): 
      return f(g, *args, **kwargs) 
     return decorator 
    return with_arguments_helper 
Các vấn đề liên quan