A primeira vez que python sabe com e contextlib.closing (reproduzido)

Reposted from: Python's first known with and contextlib.closing_ouyangbro's blog - CSDN blog

Deixe-me apresentar brevemente o processo de minha compreensão do contextlib. Acho que esse lib integrado é bastante interessante.

1.
Antes de mim, só sei que com será usado para fechar arquivos e recursos de banco de dados, o que é muito bom.
With pode ser usado desde que uma classe que implemente ambos os métodos __enter__() e __exit__() possa facilmente criar um gerenciador de contexto.

2.
Quando abro dois bancos de dados, ambos 

com xxx como conn1:
    com yyy como conn2:
        o código
é realmente estúpido como um cachorro velho, na verdade pode:
com xxx como conn1, yyy como conn2:
    código

3.
Sempre sinto que se eu sair do bloco with, os recursos do corpo da instrução (arquivos, conexões de banco de dados, solicitações de rede) serão automaticamente fechados.
Mas um dia eu vi contextlib.closing(). Com uma cara de estupefação, por que você precisa disso quando está com? Este é o verdadeiro sistema operacional em meu coração. .
de contextlib importar fechamento
de urllib2 importar urlopen
 
com fechamento(urlopen('http://www.python.org';)) as page:
    for line in page:
        print(line)
Deixe-me negar minha ideia primeiro, qualquer uso de with vai Está tudo bem, feche automaticamente para mim.
class Door(object):
    def open(self):
        print 'Door is open'
 
    def close(self):
        print 'Door is closed'
 
with Door() as d:
    d.open()
Result:
# Error:
Traceback (most last call last):
  Arquivo "1.py", linha 38, em <module>
    com Door() como d:
AttributeError: __exit__
Com certeza, porque o método __enter__ é executado antes que o corpo da instrução with seja executado e o método __exit__ é executado após a execução do corpo da instrução with.
Se uma classe como Door não tiver esses dois métodos, ela não será elegível para uso. 

4.
Bem, conheça contextlib formalmente: https://docs.python.org/dev/library/contextlib.html
Existem algumas classes que não possuem os dois métodos acima, mas existe close(), você pode fazer sem adicionar código No caso de usar com?
Can: 
class Porta(objeto):
    def open(self):
        print 'A porta está aberta'
 
    def close(self):
        print 'A porta está fechada'
 
com contextlib.closing(Door()) as door:
    door.open()
result :
A porta está aberta
A porta está fechada
contextlib.closing(xxx), o princípio é o seguinte:
class close(object):
    """Contexto para fechar algo automaticamente no final de um bloco.
    Código como este:
        com fechamento(<módulo >.open (<argumentos>)) como f:


        f = <módulo>.open(<argumentos>)
        tente:
            <bloco>
        finalmente:
            f.close()
    """
    def __init__(self, coisa):
        self.thing = coisa
    def __enter__(self):
        return self.thing
    def __exit__(self, *exc_info):
        self.thing.close()
Este contextlib.closing() irá ajudá-lo a adicionar __enter__() e __exit__() para fazê-lo atender à condição de with.

5.
É verdade que só as turmas podem usufruir da comodidade do with? Posso usar apenas um método?
OK! Agora que você conhece contextlib.closing(), você deve conhecer contextlib.contextmanager
, que é um decorador que pode transformar um func() em uma instância de classe que satisfaça a condição with... 

! ! ! Este func() deve ser um gerador...
A primeira metade de yield é usada para indicar __enter__()
A segunda metade de yield é usada para indicar __exit__() 
de contextlib import contextmanager
 
@contextmanager
def tag(name):
    print(" <%s>" % nome)
    yield
    print("</%s>" % nome)
 
with tag("h1"):
    print 'olá mundo!'
Resultado:
<h1>
olá mundo!
</h1>

Uau, isso é muito legal. No futuro, você pode usar este gerenciador de contexto para fazer coisas que só podem ser feitas por decoradores,
como adicionar cálculo de tempo e custo a um trecho de código:
decorator version: 
import time
def wrapper(func) :
    def new_func(* args, **kwargs):
        t1 = time.time()
        ret = func(*args, **kwargs)
        t2 = time.time()
        print 'cost time=', (t2-t1)
        return ret
    return new_func
 
@wrapper
def hello(a,b):
    time.sleep(1)
    print 'a + b = ', a+b
 
hello(100,200)
result:
a + b = 300
cost time= 1.00243401527

versão contextmanger:
from contextlib import contextmanager
 
@contextmanager
def cost_time():
    t1 = time.time()
    yield
    t2 = time.time()
    print 'cost time=',t2-t1
 
with cost_time():
    time.sleep(1)
    a = 100
    b = 200
    print 'a + b = ', a + b
resultado:
a + b = 300
custo tempo= 1.00032901764
Claro, é conveniente e bonito usar decoradores~

Este é o princípio do gerenciador de contexto:
1. Como func() já é um gerador, ao executar __enter__(), o gerenciador de contexto chamará self.gen.next() para executar o rendimento de func, parar e suspender, isso já existe t1=time.time()
2. Em seguida, execute a instrução no corpo da instrução with, ou seja, a+b=300
3. Ao executar __exit__() após a execução, o gerenciador de contexto chama self.gen.next( ) comece a partir da próxima frase do rendimento de func até o final. Neste momento, com t2=time.time(), t2-t1 percebe o efeito da contagem de cost_time, o que é perfeito. 
Código fonte:
class GeneratorContextManager(object):
    """Helper for @contextmanager decorator."""
 
    def __init__(self, gen):
        self.gen = gen
 
    def __enter__(self):
        try:
            return self.gen.next()
        exceto StopIteration:
            raise RuntimeError("gerador não rendeu")
 
    def __exit__(self, type, value, traceback):
        if type is None:
            try:
                self.gen.next()
            except StopIteration:
                return
            else:
                raise RuntimeError("gerador não parou")
        else:
            if value is None:
                # É necessário forçar a instanciação para que possamos
                # informar com segurança obtemos a mesma exceção back
                value = type()
            try:
                self.gen.throw(type, value, traceback)
                raise RuntimeError("gerador não parou após throw()")
            exceto StopIteration, exc:
                return exc não é valor
            exceto:
                se sys.exc_info()[1] não for um valor:
                    raise
 
 
def contextmanager(func):
    @wraps(func)
    def helper(*args, **kwds):
        return GeneratorContextManager(func(*args, **kwds) )
    auxiliar de retorno


Acima
————————————————
Declaração de direitos autorais: Este artigo é um artigo original do blogueiro CSDN "ouyangbro", seguindo o contrato de direitos autorais CC 4.0 BY-SA, anexe o link da fonte original e este artigo para declaração de reimpressão.
Link original: https://blog.csdn.net/emaste_r/article/details/78105713

Acho que você gosta

Origin blog.csdn.net/bodybo/article/details/124208083
Recomendado
Clasificación