Реалізація використання 'with object () як f' у користувацькому класі в python


80

Мені потрібно відкрити файлоподібний об'єкт у python (це послідовне з'єднання через / dev /), а потім закрити його. Це робиться кілька разів у кількох методах мого класу. Як я це робив, відкривав файл у конструкторі, а потім закривав його в деструкторі. Однак я отримую дивні помилки, і я думаю, що це пов’язано зі збирачем сміття, і тому я все ще не звик точно не знати, коли мої об’єкти видаляються = \

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

Я шукав в Інтернеті, і я не міг знайти справді хорошої відповіді про те, як withреалізований синтаксис. Я побачив, що він використовує методи __enter__(self)і __exit(self)__. Але чи все, що мені потрібно зробити, щоб реалізувати ці методи, і я можу використовувати синтаксис? Або це ще щось?

Чи є приклад того, як це зробити, або якась документація про те, як це реалізовано на файлових об’єктах, яку я можу переглянути?

Відповіді:


106

Ці методи - це майже все, що потрібно для того, щоб об’єкт працював із 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()

23
def __enter__(self): return selfякщо потрібно посилання на Meterв блоці with.
Моргот

39

Найпростішим може бути використання стандартного модуля бібліотеки 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декоратор робить з «одинарної yield» функції генератора ви пишете) - це робить його так набагато легше відокремити вхід і вихід з стану, дозволяє уникати гніздування тощо.


1
Це набагато простіше, ніж підхід на основі класу.
byxor

6
Для забезпечення theobj.close()виконується навіть в разі виключення, ви можете обернути yield theobjз try...finallyблоком.
akesfeden

0

Перший хіт Google (для мене) пояснює це досить просто:

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

і PEP пояснює це більш точно (але також і детальніше):

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


2
Я насправді бачив їх і не думав, що жоден з них був абсолютно чітким. Перший майже говорить: "Існують методи, що називаються _ enter_ і _ exit_ " і майже нічого не пояснює про них. І PEP майже в
основному

Ні, це просто з прикладами та поясненнями. Вам потрібно прочитати його ще раз; це не складна особливість.
Glenn Maynard

5
Незважаючи на це, ви повинні знати, що відповіді лише на посилання погано сприймаються громадою загалом. Принаймні поділіться чимось із джерела, що підтримує ваші твердження.
Анотація
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.