2009-11-23 24 views
40

Tôi gặp sự cố khi sử dụng docstrings with decorators. Với ví dụ sau:Tài liệu xử lý trang trí bằng Python

def decorator(f): 
    def _decorator(): 
     print 'decorator active' 
     f() 
    return _decorator 

@decorator 
def foo(): 
    '''the magic foo function''' 
    print 'this is function foo' 

help(foo) 

Bây giờ sự giúp đỡ không chỉ cho tôi những docstring của foo như mong đợi, nó cho thấy:

Help on function _decorator in module __main__: 

_decorator() 

Nếu không có sự trang trí, sự giúp đỡ là chính xác:

Help on function foo in module __main__: 

foo() 
    the magic foo function 

Tôi biết, chức năng foo được bao quanh bởi trang trí và do đó đối tượng chức năng không còn là chức năng foo nữa. Nhưng một giải pháp tốt đẹp để có được docstring (và sự giúp đỡ) như mong đợi là gì?

Trả lời

62

Sử dụng functools.wraps() để cập nhật các thuộc tính của trang trí:

from functools import wraps 

def decorator(f): 
    @wraps(f) 
    def _decorator(): 
     print 'decorator active' 
     f() 
    return _decorator 

@decorator 
def foo(): 
    '''the magic foo function''' 
    print 'this is function foo' 

help(foo) 

Cũng thấy Standard Library documentation cho functools.

+0

Điều này không hoạt động nếu 'foo' nhận bất kỳ đối số nào - chúng được thay thế bằng bất kỳ' _decorator' nào sử dụng. Đây là một vấn đề đặc biệt là khi bạn muốn trang trí của bạn để có '* args, ** kwds'. Tôi đã không bao giờ có thể tìm thấy một cách để có được docstring đúng bằng cách sử dụng 'functools.wraps'. –

+3

@Scott Griffiths: Chuỗi tài liệu sẽ vẫn đúng ngay cả khi 'foo' nhận đối số. Tuy nhiên, 'help (foo)' sẽ hiển thị danh sách tham số của '_decorator', vì nó thực sự thay thế hàm' foo'. Không có cách nào tốt xung quanh điều này nếu bạn đang viết trang trí có tham số tùy ý bằng cách sử dụng '* args, ** kwargs', nhưng đối với tôi điểm quan trọng là docstring được giữ nguyên vẹn. Chi tiết tham số luôn có thể được chỉ định trong docstring để làm rõ. –

+0

Cảm ơn bạn đã cung cấp thêm thông tin. Gần đây tôi đã không nhận được mô tả trợ giúp chính xác cho các chức năng trang trí - có vẻ như là một tình trạng khá nghèo nàn, nhưng tôi hiểu khó khăn khi chức năng trang trí có thể có chữ ký hoàn toàn khác. Tuy nhiên, phải có một cách ... :) –

12

Tôi tìm thấy một giải pháp, nhưng không biết nếu nó thật sự tốt đẹp:

def decorator(f): 
    def _decorator(): 
     print 'decorator active' 
     f() 
    _decorator.__name__=f.__name__ 
    _decorator.__doc__=f.__doc__ 
    return _decorator 

Phần với _decorator.__name__=f.__name__ dường như một chút gớm ghiếc ... Bạn nghĩ gì?

+4

Trong thực tế đây là (gần?) Chính xác những gì functools.wraps làm :) – thomas

+2

Nó không nhìn gớm ghiếc với tôi. Nó nói chính xác những gì bạn muốn nó nói. "Tôi muốn tên của chức năng này là 'chức năng' thay vì '_decorator'." – jcdyer

-5

Đây có thể là một chút cũ bây giờ nhưng đây là cách bạn làm điều đó. Chỉ cần chắc chắn rằng @decorator được thụt vào trên cùng một dòng như def decorator(f):

from functools import wraps 

def decorator(f): 
    @wraps(f) 
    def _decorator(): 
     print 'decorator active' 
     return f() 
    return _decorator 

@decorator 
def foo(): 
    '''the magic foo function''' 
    print 'this is function foo' 

help(foo) 
+8

Đây chỉ là lặp lại câu trả lời đã được chấp nhận. –