2015-06-05 15 views
7

Có cách nào trong Python để viết một lớp sẽ lỗi trừ khi nó được sử dụng với câu lệnh with?Viết một lớp Python chỉ có thể được sử dụng như một trình quản lý ngữ cảnh

# Okay: 
with Foo() as f1: 
    f1.func1() 
    f1.func2() 

# Not okay: 
f2 = Foo() 
f2.func1() 

Tôi có thể thực hiện thủ công: có __enter__ đặt cờ và có mọi phương pháp khác kiểm tra cờ đó. Nhưng có cách nào tốt hơn để làm điều đó?

Dưới đây là mã cho tương lai không-quá-từ cách:

class Foo(object): 
    def __init__(self): 
     self._entered = False 

    def __enter__(self): 
     self._entered = True 
     return self 

    def _verify_entered(self): 
     if not self._entered: 
      raise Exception("Didn't get call to __enter__") 

    def __exit__(self, typ, val, traceback): 
     self._verify_entered() 
     print("In __exit__") 

    def func1(self): 
     self._verify_entered() 
     # do stuff ... 

    def func2(self): 
     self._verify_entered() 
     # do other stuff 
+0

Bạn có cần 'foo()' để gây ra lỗi hoặc 'f2.func1()' có được không? – Blckknght

+0

@Blckknght, có Foo() gây ra lỗi sẽ chỉ có thể thích hợp hơn để có f2.func1() làm điều đó – kuzzooroo

+2

Bạn có thể sử dụng trang trí hoặc metaclass để áp dụng séc tự động thay vì theo cách thủ công. – agf

Trả lời

3

Về mặt kỹ thuật, tôi nghĩ rằng agf có quyền theo nghĩa là bạn có thể sử dụng một metaclass để tự động hóa nội dung. Tuy nhiên, nếu tôi hiểu được động lực cơ bản đằng sau nó một cách chính xác, tôi sẽ đề nghị một cách khác.

Giả sử bạn có lớp học Payload mà bạn muốn bảo vệ thông qua trình quản lý ngữ cảnh. Trong trường hợp này, bạn chỉ cần tạo trình quản lý ngữ cảnh trả về:

# This should go in a private module. 
class Payload(object): 
    def __init__(self): 
     print 'payload ctor' 

# This should go in the public interface. 
class Context(object): 
    def __init__(self): 
     # Set up here the parameters. 
     pass 

    def __enter__(self): 
     # Build & return a Payload object 
     return Payload() 

    def __exit__(self, exc_type, exc_val, exc_tb): 
     # Cleanup 
     pass 

with Context() as f: 
    # f here is a Payload object. 

Nếu bạn ẩn Payload trong mô-đun riêng, bạn tốt.

2

Bạn có thể có phương pháp __enter__ của bạn trả về một đối tượng khác với self nếu bạn không muốn người dùng để có thể gọi các phương thức trên chính đối tượng quản lý ngữ cảnh.

class Foo(object): 
    def __enter__(self): 
     print("In __enter__") 
     return Bar() 

    def __exit__(self, typ, val, traceback): 
     print("In __exit__") 

class Bar(object): 
    def func1(self): 
     print("In func1") 

    def func2(self): 
     print("In func2") 

Bạn có thể dĩ nhiên có FooBar lớp nhiều ràng buộc với nhau hơn là tôi phải cho ví dụ này. Ví dụ: lớp Foo có thể tự vượt qua để tạo phương thức khởi tạo Bar trong __enter__.

Gọi Foo().func1() sẽ không hoạt động vì lý do rõ ràng là Foo không có bất kỳ phương pháp nào như vậy. Nếu bạn muốn Bar không hiển thị với người dùng, bạn có thể đặt trước tên của nó bằng dấu gạch dưới (gợi ý rằng nội bộ) hoặc thậm chí lồng trong lớp Foo (hoặc thậm chí là phương pháp __enter__, nếu bạn thực sự muốn cực đoan).

+0

:-) Tôi dường như đã đưa ra câu trả lời chính xác giống như bạn. –

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