Triển khai sử dụng 'with object () as f' trong lớp tùy chỉnh trong python


79

Tôi phải mở một đối tượng giống tệp trong python (đó là một kết nối nối tiếp thông qua / dev /) và sau đó đóng nó. Điều này được thực hiện nhiều lần trong một số phương thức của lớp tôi. Tôi đã làm như thế nào là mở tệp trong hàm tạo, rồi đóng tệp trong hàm hủy. Mặc dù vậy, tôi đang gặp những lỗi kỳ lạ và tôi nghĩ nó liên quan đến bộ thu gom rác và như vậy, tôi vẫn chưa quen với việc không biết chính xác khi nào các đối tượng của mình bị xóa = \

Lý do tôi làm điều này là bởi vì tôi phải sử dụng tcsetattrvới một loạt các tham số mỗi khi tôi mở nó và nó gây khó chịu khi làm tất cả những điều đó ở khắp nơi. Vì vậy, tôi muốn triển khai một lớp bên trong để xử lý tất cả những điều đó để tôi có thể sử dụng nó làm
with Meter('/dev/ttyS2') as m:

Tôi đã tìm kiếm trực tuyến và tôi không thể tìm thấy câu trả lời thực sự tốt về cách withthực hiện cú pháp. Tôi thấy rằng nó sử dụng các phương thức __enter__(self)__exit(self)__. Nhưng có phải tất cả những gì tôi phải làm để triển khai các phương thức đó và tôi có thể sử dụng cú pháp with không? Hoặc là có nhiều đến nó?

Có một ví dụ về cách thực hiện điều này hoặc một số tài liệu về cách nó được triển khai trên các đối tượng tệp mà tôi có thể xem xét không?

Câu trả lời:


105

Những phương thức đó là tất cả những gì bạn cần để làm cho đối tượng hoạt động với withcâu lệnh.

Trong __enter__bạn phải trả lại đối tượng tệp sau khi mở và thiết lập nó.

Trong __exit__bạn phải đóng đối tượng tệp. Mã để ghi vào nó sẽ nằm trongwith câu lệnh.

class Meter():
    def __init__(self, dev):
        self.dev = dev
    def __enter__(self):
        #ttysetattr etc goes here before opening and returning the file object
        self.fd = open(self.dev, MODE)
        return self.fd
    def __exit__(self, type, value, traceback):
        #Exception handling here
        close(self.fd)

meter = Meter('dev/tty0')
with meter as m:
    #here you work with the file object.
    m.read()

23
def __enter__(self): return selfnếu bạn muốn tham chiếu đến Metertrong khối with.
Morgoth

39

Dễ dàng nhất có thể là sử dụng ngữ cảnh mô-đun thư viện Python tiêu chuẩn :

import contextlib

@contextlib.contextmanager
def themeter(name):
    theobj = Meter(name)
    yield theobj
    theobj.close()  # or whatever you need to do at exit

Điều này không làm cho Meterchính nó trở thành trình quản lý ngữ cảnh (và do đó không xâm phạm đến lớp đó), mà là "trang trí" nó (không phải theo nghĩa "cú pháp trang trí" của Python, mà là gần như, nhưng không hoàn toàn, theo nghĩa của các mẫu thiết kế trang trí ;-) với một chức năng nhà máy themeter một người quản lý ngữ cảnh (mà contextlib.contextmanagertrang trí xây dựng từ "đơn yieldchức năng máy phát điện" bạn viết) - điều này làm cho nó rất dễ dàng hơn nhiều để tách nhập và điều kiện thoát, tránh làm tổ, & c.


1
Điều này đơn giản hơn nhiều so với cách tiếp cận dựa trên lớp.
byxor

6
Để đảm bảo theobj.close()được thực thi ngay cả trong trường hợp ngoại lệ, bạn có thể bọc yield theobjbằng một try...finallykhối.
akesfeden

0

Lần truy cập đầu tiên của Google (đối với tôi) giải thích nó đủ đơn giản:

http://effbot.org/zone/python-with-statement.htm

và PEP giải thích nó chính xác hơn (nhưng cũng dài dòng hơn):

http://www.python.org/dev/peps/pep-0343/


2
Tôi thực sự đã nhìn thấy những thứ đó và không nghĩ rằng cả hai đều rõ ràng cả. Đầu tiên khá nhiều nói rằng "Có các phương thức được gọi là _ enter_ và _ exit_ " và không giải thích gì về chúng. Và PEP khá nhiều chỉ nói về sự cần thiết của các cú pháp mới
Falmarri

Không, nó đơn giản với các ví dụ và giải thích. Bạn cần phải đọc lại nó; đây không phải là một tính năng phức tạp.
Glenn Maynard

5
Dù vậy, bạn nên biết rằng các câu trả lời chỉ có liên kết thường ít được cộng đồng đón nhận. Ít nhất hãy chia sẻ điều gì đó từ nguồn hỗ trợ khẳng định của bạn.
IAbstract
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.