Понимание инструкции Python с выражением
Я пытаюсь понять, есть ли разница между ними и какая разница.
Вариант один:
file_obj = open('test.txt', 'r')
with file_obj as in_file:
print in_file.readlines()
Вариант второй:
with open('test.txt', 'r') as in_file:
print in_file.readlines()
Я понимаю, что с Вариантом Один, file_obj
находится в закрытом состоянии после блока с.
Ответы
Ответ 1
Я не знаю, почему никто еще не упомянул об этом, потому что он фундаментален для способ with
работает. Как и во многих языковых функциях в Python, with
за кулисами вызывает специальные методы, которые уже определены для встроенных объектов Python и могут быть переопределяется определяемыми пользователем классами. В with
конкретном случае (и в контекстных менеджерах в целом) методы __enter__
и __exit__
.
Помните, что в Python все это объект - даже литералы. Вот почему вы можете делать такие вещи, как 'hello'[0]
. Таким образом, не имеет значения, используете ли вы файл файл непосредственно в качестве возвращаемого open
:
with open('filename.txt') as infile:
for line in infile:
print(line)
или сначала сохраните его с другим именем (например, чтобы разбить длинную строку):
the_file = open('filename' + some_var + '.txt')
with the_file as infile:
for line in infile:
print(line)
Потому что конечным результатом является the_file
, infile
, а возвращаемое значение open
все указывает на один и тот же объект и что with
вызывает методы __enter__
и __exit__
на, Встроенный метод объекта закрывает файл.
Ответ 2
Они ведут себя одинаково. Как правило, значение кода Python не изменяется, присваивая выражение переменной в той же области видимости.
Это по той же причине, что они идентичны:
f = open("myfile.txt")
против
filename = "myfile.txt"
f = open(filename)
Независимо от того, добавляете ли вы псевдоним, значение кода остается неизменным. Диспетчер контекста имеет более глубокий смысл, чем передача аргумента функции, но принцип тот же: маска управления контекстом применяется к одному и тому же объекту, и файл закрывается в обоих случаях.
Единственная причина выбора одного над другим - если вы чувствуете, что это помогает ясности кода или стиля.
Ответ 3
Нет никакой разницы между двумя способами: любой способ закрыть файл при выходе из блока.
Второй пример, который вы даете, - это типичный способ использования файлов в Python 2.6 и более поздних версиях (когда был добавлен синтаксис with
).
Вы можете проверить, что первый пример также работает в сеансе REPL следующим образом:
>>> file_obj = open('test.txt', 'r')
>>> file_obj.closed
False
>>> with file_obj as in_file:
... print in_file.readlines()
<Output>
>>> file_obj.closed
True
Итак, после выхода блоков with
, файл закрыт.
Обычно второй пример - это то, как вы это делаете.
Нет причин создавать эту дополнительную переменную file_obj
... все, что вы могли бы сделать с ней после окончания блока with
, который вы могли бы просто использовать in_file
для, потому что он все еще в области видимости.
>>> in_file
<closed file 'test.txt', mode 'r' at 0x03DC5020>
Ответ 4
Если вы просто запускаете Python и используете любой из этих параметров, то сетевой эффект будет таким же, если базовый экземпляр объекта Python file
не изменяется. (В варианте 1 файл закрывается только тогда, когда file_obj
выходит за пределы области действия vs в конце блока во втором варианте, как вы уже заметили.)
Однако могут быть различия с вариантами использования с менеджером контекста. Поскольку file
является объектом, вы можете изменить его или подклассировать его.
Вы также можете открыть файл, просто позвонив file(file_name)
, показывая, что file
действует как другие объекты (но никто не открывает файлы таким образом в Python, если он не с with
):
>>> f=open('a.txt')
>>> f
<open file 'a.txt', mode 'r' at 0x1064b5ae0>
>>> f.close()
>>> f=file('a.txt')
>>> f
<open file 'a.txt', mode 'r' at 0x1064b5b70>
>>> f.close()
В общем случае открытие и закрытие некоторого ресурса, называемого the_thing
(обычно файл, но может быть любым), вы выполняете следующие действия:
set up the_thing # resource specific, open, or call the obj
try # generically __enter__
yield pieces from the_thing
except
react if the_thing is broken
finally, put the_thing away # generically __exit__
Вы можете с легкостью изменить поток этих подэлементов с помощью диспетчера контекста и процедурного кода, сплетенного между open
и другими элементами кода.
Так как Python 2.5, файловые объекты имеют методы __enter__ и __exit__:
>>> f=open('a.txt')
>>> f.__enter__
<built-in method __enter__ of file object at 0x10f836780>
>>> f.__exit__
<built-in method __exit__ of file object at 0x10f836780>
Объект Python file
по умолчанию использует эти методы таким образом:
__init__(...) # performs initialization desired
__enter__() -> self # in the case of `file()` return an open file handle
__exit__(*excinfo) -> None. # in the case of `file()` closes the file.
Эти методы могут быть изменены для вашего собственного использования, чтобы изменить способ обращения с ресурсом при его открытии или закрытии. Диспетчер контекста позволяет очень легко изменять, что происходит, когда вы открываете или закрываете файл.
Тривиальный пример:
class Myopen(object):
def __init__(self, fn, opening='', closing='', mode='r', buffering=-1):
# set up the_thing
if opening:
print(opening)
self.closing=closing
self.f=open(fn, mode, buffering)
def __enter__(self):
# set up the_thing
# could lock the resource here
return self.f
def __exit__(self, exc_type, exc_value, traceback):
# put the_thing away
# unlock, or whatever context applicable put away the_thing requires
self.f.close()
if self.closing:
print(self.closing)
Теперь попробуйте следующее:
>>> with Myopen('a.txt', opening='Hello', closing='Good Night') as f:
... print f.read()
...
Hello
[contents of the file 'a.txt']
Good Night
Как только вы контролируете запись и выходите на ресурс, существует много вариантов использования:
- Заблокировать ресурс для доступа к нему и использовать его; разблокировать, когда вы закончите.
- Сделать причудливый ресурс (например, файл памяти, базу данных или веб-страницу) более похожи на прямой файловый ресурс
- Откройте базу данных и откат, если есть исключение, но совершите все записи, если ошибок нет.
- Временное изменение контекста вычисления с плавающей запятой
- Время фрагмента кода
- Измените исключения, которые вы повышаете, возвращая
True
или False
из метода __exit__.
Вы можете прочитать больше примеров в PEP 343.