Suponha que eu tenha uma função que faça coisas com um arquivo de texto - por exemplo, leia e remova a palavra 'a'. Eu poderia passar um nome de arquivo para ele e lidar com a abertura / fechamento da função, ou poderia passar o arquivo aberto e esperar que quem telefonasse tratasse de fechá-lo.
A primeira maneira parece ser a melhor maneira de garantir que nenhum arquivo seja deixado aberto, mas me impede de usar coisas como objetos StringIO
A segunda maneira pode ser um pouco perigosa - não há como saber se o arquivo será fechado ou não, mas eu seria capaz de usar objetos semelhantes a arquivos
def ver_1(filename):
with open(filename, 'r') as f:
return do_stuff(f)
def ver_2(open_file):
return do_stuff(open_file)
print ver_1('my_file.txt')
with open('my_file.txt', 'r') as f:
print ver_2(f)
Um destes é geralmente preferido? É geralmente esperado que uma função se comporte de uma dessas duas maneiras? Ou deveria apenas ser bem documentado, para que o programador possa usar a função conforme apropriado?
fonte
your_function
pode ser usado nesse sentido.A verdadeira questão é de completude. Sua função de processamento de arquivos é o processamento completo do arquivo ou é apenas uma parte de uma cadeia de etapas de processamento? Se estiver completo por si só, fique à vontade para encapsular todo o acesso a arquivos em uma função.
Isso tem a propriedade muito agradável de finalizar o recurso (fechando o arquivo) no final da
with
instrução.Se, no entanto, houver a necessidade de processar um arquivo já aberto, a distinção entre você
ver_1
ever_2
faz mais sentido. Por exemplo:Esse tipo de teste explícito de tipo geralmente é desaprovado , especialmente em linguagens como Java, Julia e Go, nas quais o envio por tipo ou por interface é diretamente suportado. No Python, no entanto, não há suporte à linguagem para envio baseado em tipo. Ocasionalmente, você pode ver críticas ao teste de tipo direto no Python, mas na prática é extremamente comum e bastante eficaz. Ele permite que uma função tenha um alto grau de generalidade, manipulando quaisquer tipos de dados que possam aparecer no seu caminho, também conhecido como "digitação de pato". Observe o sublinhado principal em
_ver_file
; essa é uma maneira convencional de designar uma função "privada" (ou método). Embora tecnicamente possa ser chamado diretamente, ele sugere que a função não se destina ao consumo externo direto.2019 atualização: Dado atualizações recentes em Python 3, por exemplo, que os caminhos estão agora potencialmente armazenados como
pathlib.Path
objetos e não apenasstr
oubytes
(3.4+), e que a indicação de tipo passou de esotérico para integrar (cerca 3.6+, embora ainda em evolução ativamente), aqui está código atualizado que leva esses avanços em consideração:fonte
read
algo que possa ser semelhante a um arquivo ou chamaropen(fileobj, 'r')
e capturar oTypeError
iffileobj
não é uma string.ver
operação independente do tipo. Também pode ser possível implementarver
através da digitação do pato, como você diz. Mas gerar e capturar exceções é mais lento que a simples inspeção de tipo, e a IMO não produz nenhum benefício específico (clareza, generalidade etc.) . "hasattr(fileobj, 'read')
teste seria digitar pato; umisinstance(fileobj, str)
teste não é. Aqui está um exemplo da diferença: oisinstance
teste falha com os nomes de arquivos unicode, poisu'adsf.txt'
não é umstr
. Você testou para um tipo muito específico. Um teste de digitação de pato, baseado em chamadasopen
ou em algumadoes_this_object_represent_a_filename
função hipotética , não teria esse problema.is_instance(x, str)
mas sim algo comois_instance(x, string_types)
, comstring_types
definido corretamente para a operação adequada entre PY2 e PY3. Dado algo que grasna como uma corda,ver
reagiria corretamente; dado algo que grasna como um arquivo, o mesmo. Para um usuário dever
, não haveria diferença - exceto que a implementação da inspeção de tipo seria executada mais rapidamente. Puristas de pato: fique à vontade para discordar.Se você passar o nome do arquivo em vez do identificador, não há garantia de que o segundo arquivo seja o mesmo que o primeiro quando ele for aberto; isso pode levar a erros de correção e falhas de segurança.
fonte
Trata-se de propriedade e a responsabilidade de fechar o arquivo. Você pode transmitir um identificador de fluxo ou arquivo ou qualquer outra coisa que deva ser fechada / descartada em algum momento para outro método, desde que você esteja claro quem é o proprietário e que ele será fechado pelo proprietário quando você terminar . Isso geralmente envolve uma construção try-finalmente ou o padrão descartável.
fonte
Se você optar por passar os arquivos abertos, poderá fazer algo como o seguinte, MAS você não terá acesso ao nome do arquivo na função que grava no arquivo.
Eu faria isso se quisesse ter uma classe que fosse 100% responsável pelas operações de arquivo / fluxo e outras classes ou funções que seriam ingênuas e não se espera que abram ou fechem esses arquivos / fluxos.
Lembre-se de que os gerentes de contexto funcionam como uma cláusula Finalmente. Portanto, se uma exceção for lançada na função de gravação, o arquivo será fechado, não importa o quê.
fonte
with open
? Como isso aborda a questão do uso de nomes de arquivos e objetos semelhantes a arquivos?with open
, certo? E o que você está defendendo efetivamente é uma função que usa apenas objetos do tipo arquivo e não se importa de onde veio?