Pergunta

Eu vi isso no código de alguém.O que isso significa?

    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
Foi útil?

Solução

Usando esses métodos mágicos (__enter__, __exit__) permite implementar objetos que podem ser usados ​​facilmente com o with declaração.

A ideia é facilitar a construção de código que precisa de algum código de 'limpeza' executado (pense nisso como um try-finally bloquear). Mais algumas explicações aqui.

Um exemplo útil poderia ser um objeto de conexão de banco de dados (que então fecha automaticamente a conexão quando a instrução 'with' correspondente sai do escopo):

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()
        ...

Conforme explicado acima, use este objeto com o with declaração (pode ser necessário fazer from __future__ import with_statement na parte superior do arquivo se você estiver no Python 2.5).

with DatabaseConnection() as mydbconn:
    # do stuff

PEP343 - A declaração 'com'' também tem um belo texto.

Outras dicas

Se você sabe o que Gerentes de contexto são então você não precisa de mais nada para entender __enter__ e __exit__ Métodos mágicos. Vamos ver um exemplo muito simples.

Neste exemplo, estou abrindo myfile.txt com ajuda de abrir função. o tente/finalmente O bloco garante que, mesmo que ocorra uma exceção inesperada myfile.txt Será fechado.

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

Agora estou abrindo o mesmo arquivo com com declaração:

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

Se você olhar para o código, eu não fechei o arquivo e não há tente/finalmente quadra. Porque com a instrução fecha automaticamente myfile.txt . Você pode até verificar ligando print(fp.closed) atributo - que retorna True.

Isso ocorre porque os objetos do arquivo (FP no meu exemplo) retornados por abrir A função tem dois métodos internos __enter__ e __exit__. Também é conhecido como gerente de contexto. __enter__ O método é chamado no início de com bloco e __exit__ O método é chamado no final. Observação: com A declaração funciona apenas com objetos que suportam o protocolo de mamanimento de contexto, ou seja, eles têm __enter__ e __exit__ métodos. Uma classe que implementa os dois métodos é conhecida como classe de gerenciador de contextos.

Agora vamos definir o nosso Gerente de contexto classe.

 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")

Espero que agora você tenha um entendimento básico de ambos __enter__ e __exit__ Métodos mágicos.

Eu achei estranhamente difícil localizar os documentos python para __enter__ e __exit__ Métodos pesquisando no Google, então ajudar os outros aqui é o link:

https://docs.python.org/2/reference/datamodel.html#with-statement-context-managers
https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers
(Os detalhes são os mesmos para ambas as versões)

object.__enter__(self)
Digite o contexto de tempo de execução relacionado a este objeto. o with A declaração vinculará o valor de retorno deste método ao (s) alvo (s) especificado na cláusula AS da instrução, se houver.

object.__exit__(self, exc_type, exc_value, traceback)
Saia do contexto de tempo de execução relacionado a este objeto. Os parâmetros descrevem a exceção que causou a saída do contexto. Se o contexto foi excitado sem exceção, todos os três argumentos serão None.

Se uma exceção for fornecida e o método deseja suprimir a exceção (ou seja, impedir que seja propagada), ele deve retornar um valor verdadeiro. Caso contrário, a exceção será processada normalmente após a saída deste método.

Observe que __exit__() Os métodos não devem reaReise a exceção aprovada; Esta é a responsabilidade do chamador.

Eu esperava uma descrição clara do __exit__ Argumentos do método. Isso está faltando, mas podemos deduzi -los ...

Presumivelmente exc_type é a classe da exceção.

Diz que você não deve reiniciar a exceção aprovada. Isso nos sugere que um dos argumentos pode ser uma instância de exceção real ... ou talvez você deve instanciá -lo do tipo e do valor?

Podemos responder olhando para este artigo:
http://effbot.org/zone/python-with-statement.htm

Por exemplo, o seguinte __exit__ O método engole qualquer TypeError, mas permite todas as outras exceções através de:

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

...tão claramente value é uma instância de exceção.

E presumivelmente traceback é um python traceback objeto.

Além das respostas acima para exemplificar a ordem de invocação, um exemplo simples de execução

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")

Produz a saída:

__init__
__enter__
body
__exit__
__del__

Um lembrete: ao usar a sintaxe with myclass() as mc, variável mc recebe o valor retornado por __enter__(), no caso acima None! Para esse uso, precisa definir o valor de retorno, como:

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

Tente adicionar minhas respostas (meu pensamento de aprender):

__enter__ e [__exit__] Ambos são métodos que são invocados na entrada e saída do corpo de "a declaração com" (PEP 343) e a implementação de ambos é chamada de gerente de contexto.

A declaração com com a pretendem ocultar o controle de fluxo da cláusula Finalmente e tornar o código inescrutável.

A sintaxe da declaração com:

with EXPR as VAR:
    BLOCK

que se traduz em (como mencionado no 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)

Experimente algum código:

>>> 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'

E agora tente manualmente (após a sintaxe traduzida):

>>> 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

o resultado do lado do servidor o mesmo de antes

Desculpe pelo meu inglês ruim e por minhas explicações pouco claras, obrigado ....

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top