2015-09-03 3 views
23

Я пытаюсь понять, есть ли разница между ними и какова эта разница.Понимание инструкции Python 'with'

Вариант один:

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 находится в закрытом состоянии после того, как с блоком.

+0

... Я не уверен. Время, чтобы запустить сеанс REPL Python и посмотреть, есть ли разница! – ArtOfWarfare

+4

Это оба примера контекстного открытия файла. Это в основном позволяет автоматически закрывать файл, когда вы больше не находитесь в контексте файла, который читается/записывается. –

+0

Не совсем те же вопросы, но могут помочь: http://stackoverflow.com/questions/1369526/what-is-the-python-keyword-with-used-for и http://stackoverflow.com/questions/3012488/what-is-the-python-with-statement-designed-for – tomasyany

ответ

26

Я не знаю, почему никто не упомянул об этом еще, потому что это основополагающее значение для the way with works. Как и во многих языковых функциях в Python, with за кулисами вызывает special methods, которые уже определены для встроенных объектов 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__. Метод __exit__ встроенного файлового объекта закрывает файл.

+0

Действительно ли метод '__enter__' для' file' вообще что-то делает? Файл открывается во время '__init__', затем закрывается в' __exit__' - я не могу думать ни о чем, что '__enter__' будет делать. – ArtOfWarfare

+1

Я верю, что он просто возвращает объект файла (сам) и ничего не делает. [Эта страница (последний абзац)] (http://effbot.org/zone/python-with-statement.htm), похоже, подтверждает это. –

+0

Ах, правильно, он должен «возвращать» то, потому что иначе имя в 'as name' просто получило бы« None ». – ArtOfWarfare

9

Они ведут себя одинаково. Как правило, значение кода Python не изменяется, присваивая выражение переменной в той же области видимости.

Это по той же причине, что они идентичны:

f = open("myfile.txt") 

против

filename = "myfile.txt" 
f = open(filename) 

Независимо от добавления ли псевдоним, значение кода остается неизменным. Диспетчер контекста имеет более глубокий смысл, чем передача аргумента функции, но принцип тот же: маска управления контекстом применяется к одному и тому же объекту, и файл закрывается в обоих случаях.

Единственная причина выбора одного над другим - если вы чувствуете, что это помогает ясности кода или стиля.

+1

Собственно, это может иметь значение, если между 'open' и' with' существует еще (потенциально неудачный) код, но в этом случае даже байт-код почти идентичен. – bereal

+0

@bereal, в этом случае *, добавив, что код * делает разницу, а не присваивает переменную. – lvc

+1

Я думаю, что это достойный ответ, но, как и в случае с Эли Корвиго, я удивлен, что вы еще не упоминали методы '__enter__' и' __exit__'. Это не совсем так, как ваш пример, потому что в вашем случае ни строковый литерал, ни значение 'filename' будут переданы функции и в любом случае переименованы в ее область. Это больше похоже на разницу между немедленным индексированием возвращаемого значения функции 'hello() [0]' в отличие от назначения затем подписи 'hello_result = hello(); hello_result [0] '. –

5

Нет никакой разницы между двумя способами: в любом случае файл закрывается при выходе из блока.

Второй пример, который вы даете, - это типичный способ использования файлов в 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> 
+0

* Нет причин для создания этой дополнительной переменной file_obj. * Что, если код где-то в строке нуждается в другом диспетчере контекстов для этого файла? –

+1

@BhargavRao в любом случае вам придется повторно открыть этот файл. 'with' не сделает этого для вас. – bereal

+0

Вы не поняли. Вместо того, чтобы снова делать 'с open ('test.txt', 'r') как in_file:' OP может непосредственно делать 'с file_obj как in_file:'. Мой аргумент состоял в том, чтобы хранить значения в переменных, а не о менеджере контекста. Я просто хотел упомянуть, что действительно существует прецедент для сохранения того, что в переменной (хотя и удаленно) –

3

Если вы просто запустите Python и использовать любой из этих вариантов, чистый эффект такой же если базовый экземпляр file объекта в Python не изменяется. (В один вариант, файл закрыт только когда file_obj выходит из области против в конце блока во втором варианте, как вы уже заметили.)

Там могут быть различия с прецедентами с context manager однако , Поскольку 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 

После того, как у вас есть контроль входа и выхода на ресурс, есть много случаев использования:

  1. замок ресурс для доступа к нему и его использования; разблокировать, когда вы закончите
  2. Сделать причудливый ресурс (например, файл памяти, базу данных или веб-страницу) более похожим на прямой файловый ресурс
  3. Открыть базу данных и откат, если есть исключение, но совершить все записи, если есть без ошибок
  4. Временно изменить контекст вычисления с плавающей запятой
  5. времени куска кода
  6. изменения исключений, которые вы поднимаете, возвращая True или False от метода __exit__.

Вы можете прочитать больше примеров в PEP 343.

Смежные вопросы