O que é python "com" instrução projetado para?
-
26-09-2019 - |
Pergunta
Me deparei com o Python with
declaração pela primeira vez hoje.Estou usando Python levemente por vários meses e nem sabia de sua existência!Dada a sua um tanto obscuro status, eu pensei que seria a pena perguntar:
- O que é Python
with
declaração projetado para ser usado? - O que fazer você usá-lo?
- Há alguma
dicas que eu preciso estar ciente de, ou
comum anti-padrões associados
seu uso?Todos os casos em que é melhor usar
try..finally
dewith
? - Por que não é usado mais amplamente?
- Que padrão de uma biblioteca de classes são compatíveis com ele?
Solução
Eu acredito que isso já foi respondido por outros usuários antes de mim, então eu só o adiciono por uma questão de completude: o
with
A declaração simplifica o manuseio de exceções, encapsulando tarefas de preparação e limpeza comuns na chamada Gerentes de contexto. Mais detalhes podem ser encontrados em PEP 343. Por exemplo, oopen
A declaração é um gerente de contexto em si, que permite abrir um arquivo, mantenha -o aberto enquanto a execução estiver no contexto dowith
Declaração onde você a usou e feche -a assim que deixar o contexto, não importa se você o deixou devido a uma exceção ou durante o fluxo de controle regular. owith
a declaração pode assim ser usada de maneiras semelhantes ao Padrão Raii em C ++: algum recurso é adquirido pelowith
declaração e liberado quando você sai dowith
contexto.Alguns exemplos são: abrindo arquivos usando
with open(filename) as fp:
, adquirindo bloqueios usandowith lock:
(Ondelock
é uma instância dethreading.Lock
). Você também pode construir seus próprios gerentes de contexto usando ocontextmanager
decorador decontextlib
. Por exemplo, costumo usar isso quando tenho que alterar o diretório atual temporariamente e depois voltar para onde estava:from contextlib import contextmanager import os @contextmanager def working_directory(path): current_dir = os.getcwd() os.chdir(path) try: yield finally: os.chdir(current_dir) with working_directory("data/stuff"): # do something within data/stuff # here I am back again in the original working directory
Aqui está outro exemplo que redireciona temporariamente
sys.stdin
,sys.stdout
esys.stderr
Para outra manipulação de arquivos e os restaura mais tarde:from contextlib import contextmanager import sys @contextmanager def redirected(**kwds): stream_names = ["stdin", "stdout", "stderr"] old_streams = {} try: for sname in stream_names: stream = kwds.get(sname, None) if stream is not None and stream != getattr(sys, sname): old_streams[sname] = getattr(sys, sname) setattr(sys, sname, stream) yield finally: for sname, stream in old_streams.iteritems(): setattr(sys, sname, stream) with redirected(stdout=open("/tmp/log.txt", "w")): # these print statements will go to /tmp/log.txt print "Test entry 1" print "Test entry 2" # back to the normal stdout print "Back to normal stdout again"
E, finalmente, outro exemplo que cria uma pasta temporária e a limpa ao sair do contexto:
from tempfile import mkdtemp from shutil import rmtree @contextmanager def temporary_dir(*args, **kwds): name = mkdtemp(*args, **kwds) try: yield name finally: shutil.rmtree(name) with temporary_dir() as dirname: # do whatever you want
Outras dicas
Eu sugeriria duas palestras interessantes:
1.o with
A instrução é usada para envolver a execução de um bloco com métodos definidos por um gerenciador de contexto. Isso permite comum try...except...finally
Padrões de uso a serem encapsulados para reutilização conveniente.
2.Você poderia fazer algo como:
with open("foo.txt") as foo_file:
data = foo_file.read()
OU
from contextlib import nested
with nested(A(), B(), C()) as (X, Y, Z):
do_something()
Ou (Python 3.1)
with open('data') as input_file, open('result', 'w') as output_file:
for line in input_file:
output_file.write(parse(line))
OU
lock = threading.Lock()
with lock:
# Critical section of code
3.Eu não vejo nenhum antipateria aqui.
Citando Mergulhe em python:
Tente .. finalmente é bom. com é melhor.
4.Eu acho que está relacionado ao hábito dos programadores usar try..catch..finally
declaração de outros idiomas.
O Python with
declaração de integrado de suporte ao idioma do Resource Acquisition Is Initialization
expressão comumente usada em C++.Destina-se a permitir a segura aquisição e liberação de recursos do sistema operacional.
O with
instrução cria recursos dentro de um escopo/bloco.Tem de escrever seu código utilizando os recursos dentro do bloco.Quando o bloco sai os recursos estão corretamente lançados, independentemente do resultado do código no bloco (isto é, se o bloco sai normalmente ou devido a uma exceção).
Muitos recursos na biblioteca em Python que obedecer o protocolo exigido pelo with
instrução e assim pode ser usado com ele out-of-the-box.No entanto, qualquer pessoa pode fazer de recursos que pode ser usada em uma instrução com implementando o bem documentado protocolo: PEP 0343
Use sempre que você adquirir recursos na sua aplicação, que deve ser explicitamente abandonada, tais como arquivos, conexões de rede, bloqueios e afins.
Um exemplo de um antipateria pode ser usar o with
dentro de um loop, quando seria mais eficiente ter o with
fora do loop
por exemplo
for row in lines:
with open("outfile","a") as f:
f.write(row)
vs.
with open("outfile","a") as f:
for row in lines:
f.write(row)
A primeira maneira é abrir e fechar o arquivo para cada row
O que pode causar problemas de desempenho em comparação com a segunda maneira com o Open e fecha o arquivo apenas uma vez.
Novamente para completar, adicionarei meu caso de uso mais útil para with
declarações.
Eu faço muita computação científica e, para algumas atividades, preciso do Decimal
Biblioteca para cálculos de precisão arbitrária. Alguma parte do meu código, preciso de alta precisão e, para a maioria das outras peças, preciso de menos precisão.
Defino minha precisão padrão para um número baixo e depois uso with
Para obter uma resposta mais precisa para algumas seções:
from decimal import localcontext
with localcontext() as ctx:
ctx.prec = 42 # Perform a high precision calculation
s = calculate_something()
s = +s # Round the final result back to the default precision
Eu uso muito isso com o teste hipergeométrico, que requer a divisão de grandes números resultantes de fatores de formulários. Quando você faz cálculos de escala genômica, você deve ter cuidado com os erros de reversão e transbordamento.
Ver PEP 343 - A declaração 'com', há uma seção de exemplo no final.
... nova declaração "com" para o idioma python para possibilitar o uso de usos padrão das instruções Try/Finalmente.
Pontos 1, 2 e 3 sendo razoavelmente bem cobertos:
4: É relativamente novo, disponível apenas em python2.6+ (ou python2.5 usando from __future__ import with_statement
)
A declaração com a declaração funciona com os chamados gerentes de contexto:
http://docs.python.org/release/2.5.2/lib/typecontextmanager.html
A idéia é simplificar o manuseio de exceções fazendo a limpeza necessária depois de deixar o bloco 'com'. Alguns dos but-ins python já funcionam como gerentes de contexto.
Outro exemplo para o suporte fora da caixa e um que pode ser um pouco desconcertante no começo quando você está acostumado à maneira como embutida open()
se comporta, são connection
Objetos de módulos populares de banco de dados, como:
o connection
Objetos são gerentes de contexto e, como tal with-statement
, no entanto, ao usar as opções acima, observe que:
Quando o
with-block
está terminado, seja com uma exceção ou sem, A conexão não está fechada. No caso dewith-block
Acabamentos com uma exceção, a transação é revertida, caso contrário, a transação será cometida.
Isso significa que o programador precisa tomar cuidado para fechar a conexão, mas permite adquirir uma conexão e usá -la em múltiplos with-statements
, como mostrado no Psycopg2 Docs:
conn = psycopg2.connect(DSN)
with conn:
with conn.cursor() as curs:
curs.execute(SQL1)
with conn:
with conn.cursor() as curs:
curs.execute(SQL2)
conn.close()
No exemplo acima, você notará que o cursor
objetos de psycopg2
também são gerentes de contexto. Da documentação relevante sobre o comportamento:
Quando um
cursor
sai dowith-block
Está fechado, liberando qualquer recurso eventualmente associado a ele. O estado da transação não é afetado.
Em python geralmente “com”A instrução é usada para abrir um arquivo, processar os dados presentes no arquivo e também para fechar o arquivo sem chamar um método Close (). A declaração "com" simplifica o manuseio de exceção, fornecendo atividades de limpeza.
Forma geral de com:
with open(“file name”, “mode”) as file-var:
processing statements
Nota: Não há necessidade de fechar o arquivo ligando para Close () Após File-Var.close ()