2016-02-02 16 views
15

Tôi có một lớp học trang trí một số phương pháp bằng cách sử dụng một trang trí từ một thư viện khác. Cụ thể, lớp này phân lớp các tài nguyên bình yên, trang trí các phương thức http với httpauth.HTTPBasicAuth().login_required() và thực hiện một số mặc định hợp lý trên một dịch vụ mô hình.Có cách nào khác để bỏ qua trang trí theo phương pháp của phân lớp không?

Trên hầu hết các lớp con tôi muốn trang trí được áp dụng; do đó tôi muốn loại bỏ nó hơn là thêm nó vào các lớp con.

Suy nghĩ của tôi là có phương pháp riêng tư hoạt động và phương pháp công khai được trang trí. Các tác dụng của trang trí có thể tránh được bằng cách ghi đè phương thức công khai để gọi riêng tư và không trang trí quá trình ghi đè này. Ví dụ được mô phỏng bên dưới.

Tôi tò mò muốn biết nếu có cách nào tốt hơn để thực hiện việc này. Có một phím tắt cho 'hủy bỏ trang trí' trong python cung cấp cho hiệu ứng này?

Hoặc bạn có thể giới thiệu phương pháp tiếp cận tốt hơn không?

Một số câu hỏi khác có câu trả lời phù hợp cho điều này, ví dụ: Is there a way to get the function a decorator has wrapped?. Nhưng câu hỏi của tôi là về thiết kế rộng hơn - tôi quan tâm đến mọi cách pythonic để chạy các hoạt động trong các phương pháp được trang trí mà không ảnh hưởng đến việc trang trí. Ví dụ. ví dụ của tôi là một trong những cách như vậy nhưng có thể có những người khác.

def auth_required(fn): 
    def new_fn(*args, **kwargs): 
     print('Auth required for this resource...') 
     fn(*args, **kwargs) 
    return new_fn 

class Resource: 
    name = None 

    @auth_required 
    def get(self): 
     self._get() 

    def _get(self): 
     print('Getting %s' %self.name) 

class Eggs(Resource): 
    name = 'Eggs' 

class Spam(Resource): 
    name = 'Spam' 

    def get(self): 
     self._get() 
     # super(Spam, self)._get() 

eggs = Eggs() 
spam = Spam() 

eggs.get() 
# Auth required for this resource... 
# Getting Eggs 

spam.get() 
# Getting Spam 
+2

Có thể trùng lặp của [Có cách nào để có được chức năng mà một người trang trí đã gói không?] (Http://stackoverflow.com/questions/1545178/is- có một công cụ trang trí-đã-được-bọc) – Oin

Trả lời

9

Flask-HTTPAuth sử dụng functools.wraps trong login_required trang trí:

def login_required(self, f): 
    @wraps(f) 
    def decorated(*args, **kwargs): 
     ... 

Từ Python 3.2, vì đây gọi update_wrapper, bạn có thể truy cập vào các chức năng ban đầu qua __wrapped__:

Để cho phép truy cập vào các chức năng ban đầu để lấy nội dung và các mục đích khác (ví dụ: bỏ qua trang trí bộ nhớ đệm chẳng hạn như lru_cache()), chức năng này tự động dds a __wrapped__ thuộc tính cho wrapper đề cập đến chức năng đang được bao bọc.

Nếu bạn đang viết trang trí của riêng mình, như trong ví dụ, bạn cũng có thể sử dụng @wraps để có cùng chức năng (cũng như lưu tài liệu, v.v.).

Xem thêm Is there a way to get the function a decorator has wrapped?

+0

Điều đó hoạt động tốt - mặc dù tôi có thể cần một giải pháp thay thế khi có nhiều trang trí hơn. Câu trả lời được chấp nhận và câu hỏi được cập nhật với cách sử dụng –

+0

@JothamApaloo miễn là chúng hoàn toàn là '@ wraps', bạn chỉ có thể tiếp tục cho đến khi bạn đạt được thứ gì đó không có thuộc tính đó! Lưu ý rằng bạn không nên đặt câu trả lời trong câu hỏi. – jonrsharpe

+0

Cảm ơn @jonrsharpe. Tôi đã suy nghĩ dọc theo dòng hủy chỉ '@ with_required' nhưng cho phép những người khác vẫn tồn tại. Tôi sẽ đối phó với nó khi cần đến và có thể cập nhật điều này. Ngoài ra, tôi muốn đặt câu trả lời trong một nhận xét nhưng nó không định dạng chính xác. Là giao thức để chỉ cần thêm nó như là một câu trả lời, sau đó? Tôi đoán đó là hiển nhiên, vì nó là một 'câu trả lời', chịu với tôi và xác minh :) –

2

Một tùy chọn khác phổ biến là có chức năng trang trí giữ một bản sao của hàm gốc có thể được truy cập:

def auth_required(fn): 
    def new_fn(*args, **kwargs): 
     print('Auth required for this resource...') 
     fn(*args, **kwargs) 
    new_fn.original_fn = fn 
    return new_fn 

Bây giờ, đối với bất kỳ chức năng đã được trang trí, bạn có thể truy cập vào original_fn của nó để có được một xử lý cho chức năng ban đầu, không được trang trí.

Trong trường hợp đó, bạn có thể xác định một số loại điều phối hoặc thực hiện cuộc gọi hàm đơn giản (khi bạn hài lòng với hành vi trang trí) hoặc gọi đến thing.original_fn khi bạn muốn tránh hành vi trang trí.

Phương pháp được đề xuất của bạn cũng là một cách hợp lệ để cấu trúc và đề xuất của tôi "tốt hơn" phụ thuộc vào phần còn lại của mã mà bạn đang xử lý, ai cần đọc và các loại cân bằng khác .

0

Tôi tò mò muốn biết nếu có một cách tốt hơn để làm điều này. Có phím tắt để 'hủy trang trí' trong python cung cấp hiệu ứng này không?

Sử dụng thư viện undecorated. Nó digs thông qua tất cả các trang trí và trả về chỉ là chức năng ban đầu. Các tài liệu nên tự giải thích, về cơ bản bạn chỉ cần gọi: undecorated(your_decorated_function)

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