Реализация использования 'with object () as f' в пользовательском классе в Python - PullRequest
48 голосов
/ 23 сентября 2010

Мне нужно открыть файл-подобный объект в python (это последовательное соединение через / dev /), а затем закрыть его. Это делается несколько раз в нескольких методах моего класса. Как я это делал, открывал файл в конструкторе, а затем закрывал его в деструкторе. Хотя я получаю странные ошибки и думаю, что это связано с сборщиком мусора и тому подобным, я до сих пор не привык точно не знать, когда удаляются мои объекты = \

Причина, по которой я это делал, заключается в том, что мне приходится использовать tcsetattr с кучей параметров каждый раз, когда я открываю его, и это раздражает, когда я все это делаю повсюду. Поэтому я хочу реализовать внутренний класс для обработки всего этого, чтобы я мог использовать его, делая
with Meter('/dev/ttyS2') as m:

Я искал онлайн и не смог найти действительно хороший ответ о том, как реализован синтаксис with. Я видел, что он использует методы __enter__(self) и __exit(self)__. Но все ли мне нужно для реализации этих методов, и я могу использовать синтаксис with? Или есть что-то еще?

Есть ли пример того, как это сделать, или некоторая документация о том, как это реализовано на файловых объектах, на которые я могу посмотреть?

Ответы [ 3 ]

68 голосов
/ 23 сентября 2010

Эти методы - почти все, что вам нужно, чтобы заставить объект работать с оператором with.

В __enter__ необходимо вернуть объект файла после его открытия и настройки.

В __exit__ необходимо закрыть объект файла.Код для записи в него будет в теле оператора with.

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()
21 голосов
/ 23 сентября 2010

Проще всего использовать стандартный библиотечный модуль Python contextlib :

import contextlib

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

Это не делает Meter менеджером контекста (и, следовательно, не является инвазивным для этогокласс), но скорее «украшает» его (не в смысле «синтаксиса декоратора» Python, а скорее почти, но не совсем, в смысле шаблона проектирования декоратора ;-) с помощью фабричной функции themeter, которая это менеджер контекста (который декоратор contextlib.contextmanager создает из функции генератора "single- yield", которую вы пишете) - это делает , поэтому намного проще разделить вход и выходусловие, избегает вложения и т. д.

2 голосов
/ 23 сентября 2010

Первый хит Google (для меня) объясняет это достаточно просто:

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

, и PEP объясняет это более точно (но также более подробно):

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

...