Розглянемо наступне:
with open(path, mode) as f:
return [line for line in f if condition]
Чи буде файл закритий належним чином, чи використовуючи return
якимось чином обхід менеджера контексту ?
Розглянемо наступне:
with open(path, mode) as f:
return [line for line in f if condition]
Чи буде файл закритий належним чином, чи використовуючи return
якимось чином обхід менеджера контексту ?
Відповіді:
Так, він діє як finally
блок після try
блоку, тобто він завжди виконується (якщо, звичайно, процес python не закінчується звичайним чином).
Він також згадується в одному з прикладів PEP-343, який є специфікацією для with
твердження:
with locked(myLock):
# Code here executes with myLock held. The lock is
# guaranteed to be released when the block is left (even
# if via return or by an uncaught exception).
Щось варто згадати, проте, що ви не можете легко зловити винятки, кинуті open()
викликом, не помістивши весь with
блок всередину try..except
блоку, який зазвичай не є тим, чого хочеться.
Process.terminate()
це один з небагатьох (єдиних?) Сценаріїв, який не гарантує виклик finally
заяви: "Зауважте, що обробники виходу та нарешті пункти тощо не будуть страчений ».
with
блоку, чи зберігається гарантія, поки генератор зберігає врожайні значення? доки що-небудь посилається на це? Тобто мені потрібно використовувати del
або призначити інше значення змінній, яка містить об’єкт генератора?
ValueError: I/O operation on closed file.
.
Так.
def example(path, mode):
with open(path, mode) as f:
return [line for line in f if condition]
.. це майже рівнозначно:
def example(path, mode):
f = open(path, mode)
try:
return [line for line in f if condition]
finally:
f.close()
Точніше, __exit__
метод у контекстному менеджері завжди викликається при виході з блоку (незалежно від винятків, повернень тощо). Метод файлового об'єкта __exit__
просто викликає f.close()
(наприклад, тут, у CPython )
finally
keywrod є: def test(): try: return True; finally: return False
.
Так. Більш загально, __exit__
метод менеджера контексту із заявою дійсно буде викликаний у випадку return
зсередини контексту. Це можна перевірити за допомогою наступного:
class MyResource:
def __enter__(self):
print('Entering context.')
return self
def __exit__(self, *exc):
print('EXITING context.')
def fun():
with MyResource():
print('Returning inside with-statement.')
return
print('Returning outside with-statement.')
fun()
Вихід:
Entering context.
Returning inside with-statement.
EXITING context.
Вихідний результат підтверджує те, що __exit__
було викликано, незважаючи на ранній return
. Таким чином, менеджер контексту не обходить стороною.
Так, але в інших випадках може бути якийсь побічний ефект, оскільки він може щось робити (наприклад, промивний буфер) в __exit__
блоці
import gzip
import io
def test(data):
out = io.BytesIO()
with gzip.GzipFile(fileobj=out, mode="wb") as f:
f.write(data)
return out.getvalue()
def test1(data):
out = io.BytesIO()
with gzip.GzipFile(fileobj=out, mode="wb") as f:
f.write(data)
return out.getvalue()
print(test(b"test"), test1(b"test"))
# b'\x1f\x8b\x08\x00\x95\x1b\xb3[\x02\xff' b'\x1f\x8b\x08\x00\x95\x1b\xb3[\x02\xff+I-.\x01\x00\x0c~\x7f\xd8\x04\x00\x00\x00'
else
можна було б додати,with
щоб вирішити цюtry with except
проблему. редагувати: додано до мови