2010-10-05 24 views
7

So với trang trí được áp dụng cho một hàm, không dễ hiểu các trang trí được áp dụng cho một lớp.Trang trí được áp dụng cho định nghĩa lớp bằng Python

@foo 
class Bar(object): 
    def __init__(self, x): 
     self.x = x 
    def spam(self): 
     statements 

Trường hợp sử dụng trang trí cho một lớp học là gì? Làm thế nào để sử dụng nó?

Trả lời

23

Nó thay thế phần lớn các ứng dụng tốt cổ điển cho metaclasses tùy chỉnh theo cách đơn giản hơn nhiều. Hãy suy nghĩ về nó theo cách này: không có gì trực tiếp trong thân thể lớp có thể ám chỉ đối tượng lớp, bởi vì đối tượng lớp không tồn tại cho đến khi cơ thể chạy xong (đó là công việc của metaclass để tạo đối tượng lớp - - thường là type 's, đối với tất cả các lớp không có metaclass tùy chỉnh).

Nhưng, mã trong trình trang trí lớp chạy sau đối tượng lớp được tạo (thực sự, với đối tượng lớp làm đối số!) Và vì vậy hoàn toàn có thể tham chiếu đến đối tượng lớp đó (và thường cần làm như vậy).

Ví dụ, hãy xem xét:

def enum(cls): 
    names = getattr(cls, 'names', None) 
    if names is None: 
    raise TypeError('%r must have a class field `names` to be an `enum`!', 
        cls.__name__) 
    for i, n in enumerate(names): 
    setattr(cls, n, i) 
    return cls 

@enum 
class Color(object): 
    names = 'red green blue'.split() 

và bây giờ bạn có thể tham khảo Color.red, Color.green, & c, chứ không phải để 0, 1, vv (Tất nhiên bạn thường sẽ thêm nhiều chức năng hơn để làm cho "một số enum", nhưng ở đây tôi chỉ hiển thị cách đơn giản để thêm tính năng bổ sung đó vào trình trang trí lớp học, thay vì cần một metaclass tùy chỉnh! -)

+0

@Manoj, vui vì bạn thích nó, tx đã cho tôi biết! –

6

Một trường hợp sử dụng tôi có thể nghĩ là nếu bạn muốn bọc tất cả các phương thức của một lớp với một trang trí hàm. Giả sử bạn có các trang trí sau:

def logit(f): 
    def res(*args, **kwargs): 
     print "Calling %s" % f.__name__ 
     return f(*args, **kwargs) 
    return res 

Và các lớp sau:

>>> class Pointless: 
    def foo(self): print 'foo' 
    def bar(self): print 'bar' 
    def baz(self): print 'baz' 

>>> p = Pointless() 
>>> p.foo(); p.bar(); p.baz() 
foo 
bar 
baz 

Bạn có thể trang trí tất cả các phương pháp:

>>> class Pointless: 
    @logit 
    def foo(self): print 'foo' 
    @logit 
    def bar(self): print 'bar' 
    @logit 
    def baz(self): print 'baz' 

>>> p = Pointless() 
>>> p.foo(); p.bar(); p.baz() 
Calling foo 
foo 
Calling bar 
bar 
Calling baz 
baz 

Nhưng đó là LAME! Thay vào đó bạn có thể làm điều này:

>>> def logall(cls): 
for a in dir(cls): 
    if callable(getattr(cls, a)): 
     setattr(cls, a, logit(getattr(cls, a))) 
return cls 

>>> @logall 
class Pointless: 
    def foo(self): print 'foo' 
    def bar(self): print 'bar' 
    def baz(self): print 'baz' 

>>> p = Pointless() 
>>> p.foo(); p.bar(); p.baz() 
Calling foo 
foo 
Calling bar 
bar 
Calling baz 
baz 

UPDATE: Một phiên bản chung chung hơn của logall:

>>> def wrapall(method): 
    def dec(cls): 
     for a in dir(cls): 
      if callable(getattr(cls, a)): 
       setattr(cls, a, method(getattr(cls, a))) 
     return cls 
    return dec 

>>> @wrapall(logit) 
class Pointless: 
     def foo(self): print 'foo' 
     def bar(self): print 'bar' 
     def baz(self): print 'baz' 

>>> p = Pointless() 
>>> p.foo(); p.bar(); p.baz() 
Calling foo 
foo 
Calling bar 
bar 
Calling baz 
baz 
>>> 

Họ và tiết lộ: Tôi chưa bao giờ phải làm điều này và tôi chỉ cần thực hiện ví dụ này lên.

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