Обходной путь для причуд в contextlib.nested - PullRequest
4 голосов
/ 25 марта 2012

Я понимаю , почему contextlib.nested устарел .

Но если я напишу программу для старой версии Python без множественной формы with (то есть <2.7),У меня (почти) нет другого выбора. </p>

Во избежание сбоя следующей конструкции:

with nested(open("f1"), open("f2")) as (f1, f2):

(f1 не будет закрыто, если открытие f2 завершится неудачно,потому что менеджер контекста не введен)

Я мог бы представить себе менеджер контекста, который перемещает инициализацию в его __enter__:

@contextmanager
def late_init(f, *a, **k):
    r = f(*a, **k)
    with r as c: yield c

Прав ли я, думая, что

with nested(late_init(open, "f1"), late_init(open, "f2")) as (f1, f2):

здесь будет достаточно, чтобы сделать его «чистым»?


Данный пример использования является лишь примером.Представьте, что у вас есть список файлов, длина которых неизвестна преждевременно.Тогда ни составной 2,7 with не может быть использован, ни вложенный способ до 2,7 с несколькими отступами with операторов.


Я, вероятно, должен быть более многословен по этому поводу.

Упомянутый способ решения проблемы на первый взгляд решает проблему: вызов функции выполняется в безопасном месте, так что сбой может быть обнаружен и соответствующим образом обработан.

Мой вопрос: устраняет ли это ошибку или я получаюдругие проблемы?

Ответы [ 2 ]

3 голосов
/ 25 марта 2012

Инструмент contextlib.nested предназначен для объединения менеджеров контекста в один. Это устарело, потому что оно было ошибочным по замыслу (именно для того случая, который вы показываете, где f1 не будет закрыто, если открытие f2 не удастся).

В вашем случае регистр не требуется. Достаточно регулярного вложения:

with open('f1') as f1:
    with open('f2') as f2:
        ...
1 голос
/ 26 марта 2012

Теперь я увидел, что мое решение (late_init()), вероятно, излечит первую причуду:

Во-первых, поскольку все менеджеры контекста создаются до вызова функции, __new__() и__init__() методы менеджеров внутреннего контекста фактически не охватываются областью действия менеджеров внешнего контекста.Это означает, например, что использование nested() для открытия двух файлов является ошибкой программирования, поскольку первый файл не будет закрываться сразу, если при открытии второго файла возникнет исключение.

Новторой:

Во-вторых, если метод __enter__() одного из менеджеров внутреннего контекста вызывает исключение, которое перехватывается и подавляется методом __exit__() одного из менеджеров внешнего контекста, этоКонструкция вызовет RuntimeError, а не пропускает тело оператора with.

(что, вероятно, вызвано пропуском yield vars из nested()), этим не покрывается.

Так что либо

  1. следует избегать использования "пожирателей исключений" в сочетании с nested(),
  2. данное nested() следует заменить на лучшее или
  3. следует избегать использования списков менеджера исключений переменной длины, а также старых версий Python.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...