2016-08-15 3 views
0

Новичка Python кодер здесь, исходя из фона Java. Я все еще озадачен этим:«с», менеджер контекста, python: что происходит в простых терминах?

with open(...) as f: 
    do_something(f) 

даже после того, как погуглить и читать некоторые из ответов здесь (я просто не мог получить мою голову вокруг них).

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

as f: 

'как' выше, как и 'как' ниже

import numpy as np 

Это просто псевдоним. 'f' не относится к файлу, а к менеджеру контекста. Диспетчер контекста, используя шаблон декоратора, реализует все методы, которые открывает файл, поэтому я могу рассматривать его как файл-объект (и получить в объекте файла, вызвав соответствующие методы, которые будут вызываться в файле внутри менеджера контекста). И, конечно, файл закрывается, когда блок завершается (вся эта точка).

Возникает вопрос: Открывает() вообще возвращает файл (или ссылку на файл) или менеджер контекста? Он возвращает менеджеров контекста в целом, и это то, что мы использовали все время, не зная об этом? Или он возвращает типы файлов, кроме этого специального контекста, когда возвращает что-то другое, как менеджер контекста.

Это где-нибудь рядом с правом? Кто-нибудь хотел бы уточнить?

+1

Файлы * являются * менеджерами контекста. – user2357112

+0

'with' после открытия просто закрывает файл после записи. – n1c9

+1

Вы читали https://www.python.org/dev/peps/pep-0343/? –

ответ

3

Объекты файлов сами являются контекстными менеджерами, в том смысле, что у них есть __enter__ и __exit__ методов. with уведомляет объект file, когда контекст вводится и выходит из него (путем вызова __enter__ и __exit__ соответственно), и это значит, что файл-файл «знает», чтобы закрыть файл. Здесь нет объекта-обертки; файловые объекты предоставляют эти два метода (в терминах Java вы можете сказать, что объекты файлов реализуют интерфейс контекстного менеджера).

Отметьте, что as is не a alias точно так же, как import module as altname; вместо этого возвращается значение из contextmanager.__enter__().Метод возвращает fileobject.__enter__()self (поэтому сам объектный файл), чтобы сделать его проще использовать синтаксис:

with open(...) as fileobj: 

fileobject.__enter__() Если не делать этого, но либо возвращается None или другой объект, вы не могли бы встраивать в open() вызов; сохранить ссылку на возвращаемый объект файла, который должен был бы присвоить результат open() к переменной, прежде чем использовать его в качестве контекста менеджера:

fileobj = open(...) 
with fileobj as something_enter_returned: 
    fileobj.write() 

или

fileobj = open(...) 
with fileobj: # no as, ignore whatever fileobj.__enter__() produced 
    fileobj.write() 

Обратите внимание, что ничто не мешает вы используете последний шаблон в своем собственном коде; вы не имеете, чтобы использовать здесь часть as target, если у вас уже есть другая ссылка на файл-объект или просто не нужно даже больше обращаться к файловому объекту.

Однако другие менеджеры контекста могут возвращать что-то другое. Некоторые разъемы базы данных возвращают курсор базы данных:

conn = database.connect(....) 
with conn as cursor: 
    cursor.execute(...) 

и выход из контекста вызывает транзакцию быть совершены или откат (в зависимости от Wether или не был исключением).

0

Контекстные менеджеры - довольно простые животные ... Это просто классы, которые определяют two separate methods (__enter__ и __exit__). Все, что возвращается с __enter__, связано с предложением as x инструкции with при выполнении инструкции with.

Вот действительно глупый пример:

>>> class CM(object): 
... def __enter__(self): 
...  print('In __enter__') 
...  return 'Hello world' 
... def __exit__(self, *args): 
...  print('In __exit__') 
... 
>>> with CM() as x: 
... print(x) 
... 
In __enter__ 
Hello world 
In __exit__ 

Вы часто будете видеть менеджер контекста просто возвращающихся self от метода __enter__, но я написал выше пример, чтобы продемонстрировать, что вы не уже к. Также обратите внимание, что вам не нужно построить менеджер контекста в with заявления, вы можете построить его загодя:

cm = CM() 
with cm as x: 
    ... 

Причиной менеджеров контекста является то, что при использовании в сочетании с with заявления, питоном гарантирует, что будет называться __exit__ (даже если исключение происходит внутри пакета with) .

file объекты реализуются с помощью контекстного менеджера API (они хорошо определены __enter__ и __exit__ методы), так file объекты являются менеджеров контекста. При использовании с оператором with, python гарантирует, что, когда выйдет with, файл будет закрыт.

Запрет на отказ от катастрофы если ваш компьютер взрывается ...

+0

Таким образом, любой класс, который реализует методы __enter__ и __exit__, выполняет контекстный менеджер. Я запутался, потому что я думаю с точки зрения наследования или реализации интерфейса, но я не вижу ничего для File. –

+0

@Detroitteatime - Я не уверен, что понимаю. На python2.5 -> python2.7, если вы наберете 'help (file)', вы увидите там '__enter__' и' __exit__'. Возможно, путаница заключается в том, что 'open' является фабричным методом, который возвращает объект файла? – mgilson

+0

@Detroitteatime: вы можете думать о менеджерах контекста как интерфейсе; файловые объекты реализуют этот интерфейс. –

1

Это самый основной менеджер контекста можно создать:

class UselessContextManager(object): 
    def __enter__(self): 
     pass 

    def __exit__(self, type, value, traceback): 
     pass 

with UselessContextManager() as nothing: 
    print(nothing is None) 

Если вы хотите, чтобы получить немного почувствовать, что на самом деле поток процесс выглядит, попробуйте это один:

class PrintingContextManager(object):           
    def __init__(self, *args, **kwargs):          
     print('Initializing with args: {} and kwargs: {}'.format(args, kwargs)) 

    def __enter__(self):              
     print('I am entering the context')          
     print('I am returning 42')            
     return 42                

    def __exit__(self, type, value, traceback):         
     print('And now I am exiting')           


print('Creating manager')              
manager = PrintingContextManager()            
print('Entering with block')             
with manager as fnord:               
    print('Fnord is {}'.format(fnord))           
    print('End of context')              
print('Out of context')               

Выход:

Creating manager 
Initializing with args:() and kwargs: {} 
Entering with block 
I am entering the context 
I am returning 42 
Fnord is 42 
End of context 
And now I am exiting 
Out of context 

Вы должны попытаться модифицировать код распечатайте type, value, traceback, а затем создайте исключение внутри блока with.

Как вы можете видеть, синтаксис with является почти просто короток для:

thing = ContextManager() 
try: 
    stuff = thing.__enter__() 
except Exception as e: 
    stuff.__exit__(type(e), e.args[0], e.__traceback__) 

Though truthfully it's a bit different

Вы можете видеть, что файлы всегда контекстные менеджеры:

>>> f = open('/tmp/spanish_inquisition.txt', 'w') 
>>> f.__enter__ 
<function TextIOWrapper.__enter__> 
>>> f.__exit__ 
<function TextIOWrapper.__exit__> 

Я не знал, что файл может быть ContextManager просто реализуя два метода, не наследуя от суперкласса или явно реализуя интерфейс. Опять же, я новичок в этом языке.

В Python является явно реализует интерфейс. В Java вы должны указать, к какому интерфейсу вы хотите придерживаться. В Python вы просто это делаете. Нужен файл-подобный объект? Добавьте метод .read(). Возможно, .seek(), .open() и .close() в зависимости от того, чего они ожидают. Но в Python ...

it = DecoyDuck() 
if it.walks_like_a_duck() and it.talks_like_a_duck() and it.quacks_like_a_duck(): 
    print('It must be a duck')