Вопрос

Я видел это в чьем-то коде.Что это значит?

    def __enter__(self):
        return self

    def __exit__(self, type, value, tb):
        self.stream.close()

from __future__ import with_statement#for python2.5 

class a(object):
    def __enter__(self):
        print 'sss'
        return 'sss111'
    def __exit__(self ,type, value, traceback):
        print 'ok'
        return False

with a() as s:
    print s


print s
Это было полезно?

Решение

Используя эти волшебные методы (__enter__, __exit__) позволяет вам реализовывать объекты, которые могут быть легко использованы с with заявление.

Идея заключается в том, что это упрощает создание кода, для выполнения которого требуется некоторый "очищенный" код (думайте об этом как try-finally блок). Еще несколько объяснений здесь.

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

class DatabaseConnection(object):

    def __enter__(self):
        # make a database connection and return it
        ...
        return self.dbconn

    def __exit__(self, exc_type, exc_val, exc_tb):
        # make sure the dbconnection gets closed
        self.dbconn.close()
        ...

Как объяснено выше, используйте этот объект с with заявление (возможно, вам потребуется сделать from __future__ import with_statement в верхней части файла, если вы используете Python 2.5).

with DatabaseConnection() as mydbconn:
    # do stuff

PEP343 - Оператор 'with''с помощью' также имеет хорошую рецензию.

Другие советы

Если ты знаешь, что контекстные менеджеры неужели тогда вам больше ничего не нужно понимать __enter__ и __exit__ магические методы.Давайте посмотрим на очень простой пример.

В этом примере я открываю myfile.txt с помощью Открыть функция.В попробуй /наконец-то блок гарантирует, что даже в случае возникновения неожиданного исключения myfile.txt будет закрыт.

fp=open(r"C:\Users\SharpEl\Desktop\myfile.txt")
try:
    for line in fp:
        print(line)
finally:
    fp.close()

Теперь я открываю тот же файл с с заявление:

with open(r"C:\Users\SharpEl\Desktop\myfile.txt") as fp:
    for line in fp:
        print(line) 

Если вы посмотрите на код, я не закрывал файл, и там нет попробуй /наконец-то блок.Потому что с инструкция автоматически закрывается myfile.txt .Вы даже можете проверить это, позвонив print(fp.closed) атрибут , который возвращает True.

Это связано с тем, что файловые объекты (fp в моем примере), возвращаемые Открыть функция имеет два встроенных метода __enter__ и __exit__.Он также известен как контекстный менеджер. __enter__ метод вызывается в начале с блокировать и __exit__ метод вызывается в конце.Примечание: с оператор работает только с объектами, которые поддерживают протокол изменения контекста, т. е.у них есть __enter__ и __exit__ методы.Класс, который реализует оба метода, известен как класс context manager.

Теперь давайте определим наш собственный контекстный менеджер класс.

 class Log:
    def __init__(self,filename):
        self.filename=filename
        self.fp=None    
    def logging(self,text):
        self.fp.write(text+'\n')
    def __enter__(self):
        print("__enter__")
        self.fp=open(self.filename,"a+")
        return self    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("__exit__")
        self.fp.close()

with Log(r"C:\Users\SharpEl\Desktop\myfile.txt") as logfile:
    print("Main")
    logfile.logging("Test1")
    logfile.logging("Test2")

Я надеюсь, теперь у вас есть базовое понимание обоих __enter__ и __exit__ магические методы.

Мне было странно трудно найти документы python для __enter__ и __exit__ методы с помощью Google, поэтому, чтобы помочь другим, вот ссылка:

https://docs.python.org/2/reference/datamodel.html#with-statement-context-managers
https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers
(детали одинаковы для обеих версий)

object.__enter__(self)
Введите контекст среды выполнения, связанный с этим объектом.В with оператор свяжет возвращаемое значение этого метода с целевыми объектами, указанными в предложении as инструкции, если таковые имеются.

object.__exit__(self, exc_type, exc_value, traceback)
Выйдите из контекста среды выполнения, связанного с этим объектом.Параметры описывают исключение, которое привело к выходу из контекста.Если контекст был закрыт без исключения, все три аргумента будут None.

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

Обратите внимание , что __exit__() методы не должны повторно вызывать переданное исключение;это входит в обязанности вызывающего абонента.

Я надеялся на четкое описание __exit__ аргументы метода.Этого не хватает, но мы можем вывести их...

Предположительно exc_type является классом исключения.

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

Мы можем ответить, просмотрев эту статью:
http://effbot.org/zone/python-with-statement.htm

Например, следующее __exit__ метод проглатывает любую ошибку типа, но пропускает все остальные исключения через:

def __exit__(self, type, value, traceback):
    return isinstance(value, TypeError)

... так ясно value является экземпляром Исключения.

И предположительно traceback это Питон обратная трассировка объект.

В дополнение к приведенным выше ответам, иллюстрирующим порядок вызова, простой пример запуска

class myclass:
    def __init__(self):
        print("__init__")

    def __enter__(self): 
        print("__enter__")

    def __exit__(self, type, value, traceback):
        print("__exit__")

    def __del__(self):
        print("__del__")

with myclass(): 
    print("body")

Производит вывод:

__init__
__enter__
body
__exit__
__del__

Напоминание:при использовании синтаксиса with myclass() as mc, переменная mc получает значение , возвращаемое __enter__(), в приведенном выше случае None!Для такого использования необходимо определить возвращаемое значение, например:

def __enter__(self): 
    print('__enter__')
    return self

попробуйте добавить мои ответы (мою мысль об обучении) :

__enter__ и [__exit__] оба являются методами, которые вызываются при входе в тело "оператор with (с)" (БОДРОСТЬ ДУХА 343) и реализация того и другого называется context manager.

оператор with предназначен для скрытия управления потоком предложения try finally и делает код непостижимым.

синтаксис оператора with следующий :

with EXPR as VAR:
    BLOCK

которые переводятся на (как указано в PEP 343) :

mgr = (EXPR)
exit = type(mgr).__exit__  # Not calling it yet
value = type(mgr).__enter__(mgr)
exc = True
try:
    try:
        VAR = value  # Only if "as VAR" is present
        BLOCK
    except:
        # The exceptional case is handled here
        exc = False
        if not exit(mgr, *sys.exc_info()):
            raise
        # The exception is swallowed if exit() returns true
finally:
    # The normal and non-local-goto cases are handled here
    if exc:
        exit(mgr, None, None, None)

попробуйте какой-нибудь код:

>>> import logging
>>> import socket
>>> import sys

#server socket on another terminal / python interpreter
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> s.listen(5)
>>> s.bind((socket.gethostname(), 999))
>>> while True:
>>>    (clientsocket, addr) = s.accept()
>>>    print('get connection from %r' % addr[0])
>>>    msg = clientsocket.recv(1024)
>>>    print('received %r' % msg)
>>>    clientsocket.send(b'connected')
>>>    continue

#the client side
>>> class MyConnectionManager:
>>>     def __init__(self, sock, addrs):
>>>         logging.basicConfig(level=logging.DEBUG, format='%(asctime)s \
>>>         : %(levelname)s --> %(message)s')
>>>         logging.info('Initiating My connection')
>>>         self.sock = sock
>>>         self.addrs = addrs
>>>     def __enter__(self):
>>>         try:
>>>             self.sock.connect(addrs)
>>>             logging.info('connection success')
>>>             return self.sock
>>>         except:
>>>             logging.warning('Connection refused')
>>>             raise
>>>     def __exit__(self, type, value, tb):
>>>             logging.info('CM suppress exception')
>>>             return False
>>> addrs = (socket.gethostname())
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> with MyConnectionManager(s, addrs) as CM:
>>>     try:
>>>         CM.send(b'establishing connection')
>>>         msg = CM.recv(1024)
>>>         print(msg)
>>>     except:
>>>         raise
#will result (client side) :
2018-12-18 14:44:05,863         : INFO --> Initiating My connection
2018-12-18 14:44:05,863         : INFO --> connection success
b'connected'
2018-12-18 14:44:05,864         : INFO --> CM suppress exception

#result of server side
get connection from '127.0.0.1'
received b'establishing connection'

а теперь попробуйте вручную (следуя синтаксису перевода):

>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #make new socket object
>>> mgr = MyConnection(s, addrs)
2018-12-18 14:53:19,331         : INFO --> Initiating My connection
>>> ext = mgr.__exit__
>>> value = mgr.__enter__()
2018-12-18 14:55:55,491         : INFO --> connection success
>>> exc = True
>>> try:
>>>     try:
>>>         VAR = value
>>>         VAR.send(b'establishing connection')
>>>         msg = VAR.recv(1024)
>>>         print(msg)
>>>     except:
>>>         exc = False
>>>         if not ext(*sys.exc_info()):
>>>             raise
>>> finally:
>>>     if exc:
>>>         ext(None, None, None)
#the result:
b'connected'
2018-12-18 15:01:54,208         : INFO --> CM suppress exception

результат на стороне сервера такой же, как и раньше

извините за мой плохой английский и мои неясные объяснения, спасибо....

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top