Реализация использования «с объектом () как f» в пользовательском классе в python

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

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

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

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


person Falmarri    schedule 22.09.2010    source источник


Ответы (3)


Эти методы — почти все, что вам нужно для того, чтобы объект работал с оператором 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()
person dekomote    schedule 22.09.2010
comment
def __enter__(self): return self, если вам нужна ссылка на Meter в блоке with. - person Morgoth; 21.11.2017

Проще всего использовать стандартный модуль библиотеки 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") -- это делает его < strong>так намного проще разделить входное и выходное условие, избежать вложенности и т. д.

person Alex Martelli    schedule 23.09.2010
comment
Это намного проще, чем классовый подход. - person byxor; 28.11.2018
comment
Чтобы гарантировать выполнение theobj.close() даже в случае исключения, вы можете обернуть yield theobj блоком try...finally. - person akesfeden; 26.02.2020

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

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

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

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

person Glenn Maynard    schedule 22.09.2010
comment
Я на самом деле видел их и не думал, что кто-то из них был очень четким. Первый в значительной степени говорит, что существуют методы, называемые _enter_ и _exit_, и почти ничего не объясняет о них. И PEP просто говорит о необходимости нового синтаксиса - person Falmarri; 23.09.2010
comment
Нет, это просто с примерами и пояснениями. Вам нужно прочитать его снова; это не сложная функция. - person Glenn Maynard; 23.09.2010
comment
Тем не менее, вы должны знать, что ответы, содержащие только ссылки, плохо воспринимаются сообществом в целом. По крайней мере поделитесь чем-нибудь из источника, подтверждающим ваши утверждения. - person IAbstract; 03.08.2019
comment
первая ссылка мертва - person Boris; 23.01.2021