2014-06-09 16 views
6
def decorator(fn): 
    def wrapper(*args, **kwargs): 
     print 'With sour cream and chives!', 
     return fn(*args, **kwargs) 
    return wrapper 

class Potato(object): 
    def __call__(self): 
     print 'Potato @ {} called'.format(id(self)) 

spud = Potato() 
fancy_spud = decorator(Potato()) 

Với mã này chúng ta có hai trường hợp của lớp callable, một được trang trí và một là đồng bằng:Tôi làm cách nào để trang trí một phiên bản của một lớp có thể gọi?

>>> spud() 
Potato @ 140408136280592 called 
>>> fancy_spud() 
With sour cream and chives! Potato @ 140408134310864 called 

Tôi tự hỏi nếu nó bằng cách nào đó được hỗ trợ để sử dụng cú pháp @decorator trên callable cho chỉ là một ví dụ - trái với việc trang trí lớp/phương pháp, áp dụng cho mọi trường hợp. Theo this câu trả lời phổ biến, @syntax chỉ đường cho:

function = decorator(function) 

Nhưng nó là một quá đơn giản? Với tất cả nỗ lực nửa nướng của tôi, dường như chỉ hoạt động khi cú pháp xảy ra trước def, class, khoảng trắng hoặc @another_decorator.

@decorator 
baked = Potato() 

Đó là SyntaxError.

baked = Potato() 
@decorator 
baked 

Cũng SyntaxError.

@decorator 
def baked(_spud=Potato()): 
    return _spud() 

Hoạt động, nhưng rất xấu và gian lận.

Trả lời

3

Bạn đặt câu hỏi rằng:

Theo câu trả lời phổ biến này, @syntax chỉ đường cho:

function = decorator(function)

Tuy nhiên, nó chính xác hơn để nói rằng

@decorator 
def function(): 
    pass 

Đường cú pháp cho:

def function(): 
    pass 
function = decorator(function) 

trang trí được thiết kế để trang trí chức năng, phương pháp hoặc lớp định nghĩa, cụ thể. Các PEP that introduced class decorators mô tả ngữ pháp:

decorated: decorators (classdef | funcdef) 

funcdef: 'def' NAME parameters ['->' test] ':' suite 

Như bạn có thể thấy, một trang trí phải đến ngay trước một classdef hoặc funcdef, vì vậy không có cách nào để sử dụng nó trực tiếp trên một thể hiện của một lớp có thể được gọi.

4

Vâng, đó là sự đơn giản hóa.Nếu chúng ta nhìn vào the grammar, decorator chỉ xuất hiện trong quy tắc decorators, mà chỉ xuất hiện như một phần của một classdef hoặc funcdef:

decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE 
decorators: decorator+ 
decorated: decorators (classdef | funcdef) 

language reference says (và tôi nghĩ rằng đây là những gì đang được lặp đi lặp lại trong câu trả lời liên quan) là rằng

@f1(arg) 
@f2 
def func(): pass 

tương đương với

def func(): pass 
func = f1(arg)(f2(func)) 

và tương tự cho các định nghĩa lớp. Nhưng điều đó không có nghĩa là cú pháp @decorator có thể được áp dụng ở bất kỳ đâu; nó chỉ hợp lệ ngay trước một định nghĩa hàm hoặc lớp.

Ngoài ra, ngay cả tài liệu chính thức cũng không chính xác; tại thời điểm trang trí được gọi là hàm (hoặc lớp) không bị ràng buộc vào không gian tên hoặc phạm vi bao quanh, do đó, các cú pháp đã cho không hoàn toàn tương đương.

Có điều gì đó thú vị về các tuyên bố defclass, mà tôi nghĩ là một phần lý do khiến chúng là các câu lệnh duy nhất được hỗ trợ bởi cú pháp @decorator: chúng là cách duy nhất trong Python liên kết tên với đối tượng biết tên đó là.

Cuối cùng, đây là một cách khác để gọi một trang trí mà bạn có thể thích:

@decorator 
class baked: 
    __metaclass__ = lambda *_: Potato() 
Các vấn đề liên quan