Excluindo o cursor usado no SearchCursor dentro da compreensão do dicionário?

12

Se é melhor abrir cursores usando uma instrução with para garantir que ela seja excluída, da seguinte maneira:

with arcpy.da.UpdateCursor(fc,fields) as cursor:

Então, se um cursor for usado como iterável em uma compreensão como esta:

d = {k:v for (k,v) in arcpy.da.SearchCursor(fc,fields)}

É necessário excluir o cursor depois de usá-lo na compreensão?

J. Flann
fonte
1
Ótima pergunta. Você está tentando lidar com bloqueios de esquema? Existem algumas postagens anteriores (principalmente desatualizadas) sobre um tópico semelhante, embora não seja possível encontrar uma fonte definitiva nos novos dacursores: sgillies.net/2011/02/01/01/get-with-it.html e help.arcgis.com/ pt-br / arcgisdesktop / 10.0 / help / index.html # //… . Em particular, veja os comentários de @JasonScheirer na parte inferior do primeiro link.
Aaron

Respostas:

13

Se é absolutamente necessário, é a pergunta errada a ser feita. A questão é se é uma boa ideia.

Como regra na programação, você deve evitar fazer coisas estranhas e usar a melhor ferramenta para o trabalho . Se algo tiver uma maneira explícita de liberar recursos, apenas torne a versão explícita e pronto:

with arcpy.da.UpdateCursor(fc,fields) as cursor:
    d = {k: v for (k,v) in cursor}

O que você pode não estar ciente é que a withcláusula realmente chama lógica adicional. Uma withcláusula requer um gerenciador de contexto, que deve ter um método __enter__(chamado quando o bloco é inserido) e __exit__(chamado quando o bloco é encerrado). Em particular, o __exit__método é chamado independentemente da ocorrência de uma exceção, garantindo que o programa sempre libere o recurso mesmo com erro. Isso fornece ao seu código uma documentação explícita de quando um recurso é adquirido e quando é lançado, além de garantir que um recurso possa ser liberado o mais rápido possível.

Por outro lado, você não pode realmente depender do tempo de execução para fechá-lo magicamente imediatamente para você. Isso ocorre porque a maneira como ele é fechado é invocando o destruidor do objeto, o que pode ou não acontecer imediatamente. O Python não oferece nenhuma garantia sobre quando um destruidor é chamado, apenas que será eventualmente quando o objeto for coletado de lixo. (Veja aqui .) Atualmente, o Python é implementado para que ocorra assim que não houver mais uma referência a um objeto. Mas é fácil propagar acidentalmente referências a um objeto, e o tempo de execução do Python pode mudar.

Considere também a manutenção a longo prazo. Não há nenhuma referência a longo prazo para isso agora, mas o que acontece em 6 meses quando você precisa modificar o código de modo que não é uma referência? E se alguém fizer isso? A pessoa que está fazendo a alteração pode não pensar em mudar para um withbloco, já que ainda não existe um. Crie um hábito de limpar seus recursos e você terá muito menos problemas com ele.

Deseja realmente vincular seu código aos detalhes de implementação da coleta de lixo? Deseja ter que pensar constantemente se você pode estar propagando acidentalmente uma referência por uma exceção? Não você não. Imagine se isso aconteceu quando o script foi chamado no ArcMap. O usuário seria forçado a fechar todo o processo apenas para liberar o arquivo. Portanto, não se coloque nessa posição. Libere o recurso explicitamente. Salvar uma linha de código não vale os riscos de problemas que ela pode causar. Os gerenciadores de contexto são o mecanismo padrão para adquirir e liberar recursos no Python, e eles fazem isso muito bem.

O ponto principal é que não divulgá-lo explicitamente é uma má ideia.

Isso, é claro, pressupõe que o código tenha alguma possibilidade de afetar outra pessoa, como colocá-lo em um script que outra pessoa precisará executar ou manter ou pode atrasar a entrega do seu trabalho se você precisar fechar o ArcMap até o fim, porque você não pode salvar suas alterações. Se você é o único que será impactado por um problema, então, por todos os meios, enfrente as boas práticas o quanto quiser.

jpmc26
fonte
3

Não, não é necessário excluir um cursordepois de usá-lo em uma compreensão. A cursoré uma instância de uma classe, que é um objeto (tudo em python é um objeto). Toda sessão python possui uma namespaceque contém referências a todos os objetos da sessão - pense nela como um dicionário em que as chaves são referências a cada objeto e os valores são os próprios objetos. Quando a 'contagem de referência' - o número de chaves que se referem a esse objeto - cai para zero, o objeto é removido e a memória re-alocada . Quando você usa a cursorem uma compreensão, não há referência a esse objeto no espaço para nome. Após a compreensão, o objeto será excluído.

Não há entrada no espaço para nome e, portanto, não é necessário excluir nada. ESRI também ilustra essa sintaxe no exemplo 2, aqui .

Para esclarecer melhor, se você executar:

>>> import arcpy
>>> f = r'C:\Workspace\study_area.shp'
>>> a = arcpy.da.SearchCursor(f, ['*'])

Você verá um arquivo .lock aparecer no diretório (verifique seu gerenciador de arquivos). A referência ao cursor é a, o que fará o cursor(e, portanto, o bloqueio) persistir até que aseja excluído. Então, quando você executa:

>>> del(a)

A entrada no espaço para nome será removida e o bloqueio será liberado (o arquivo .lock desaparecerá). Se você executar:

>>> t = [i for i in arcpy.da.SearchCursor(f, ['*'])]

Você não verá um arquivo de bloqueio ou ele desaparecerá quando o comando for concluído. Sem uma entrada no espaço para nome, o cursornão é persistente. trefere-se à lista que você acabou de criar, não ao cursorusado para criá-la.

Para resumir, você só precisa se preocupar em excluir cursorsquando eles têm uma referência no espaço para nome (ou seja, quando você os atribuiu a uma variável, como ano exemplo acima).

Chris
fonte
2
Essa é uma prática de programação extremamente ruim. Se algo tem uma maneira explícita de liberar recursos, você o usa .
jpmc26
@ jpmc26, Qual parte é "prática de programação extremamente ruim"? Compreensões em geral? Ou apenas se o iterável for instanciado dentro da compreensão? Eu pensei que um argumento forte para o último é que ele libera imediatamente o recurso.
Tom
@ Tom Não está liberando recursos explicitamente. Compreensões são ferramentas fantásticas, e instanciar iteráveis ​​normais dentro delas é completamente normal. O que é ruim aqui é que os objetos do cursor adquirem bloqueios de arquivos e não há liberação explícita deles. Veja minha resposta para mais detalhes.
jpmc26
2

Os cursores de atualização e inserção não podem ser criados para uma tabela ou classe de recurso se existir um bloqueio exclusivo para esse conjunto de dados. As funções UpdateCursor ou InsertCursor falham devido a um bloqueio exclusivo no conjunto de dados. Se essas funções criarem com êxito um cursor, elas aplicarão um bloqueio exclusivo no conjunto de dados para que dois scripts não possam criar uma atualização ou inserir cursor no mesmo conjunto de dados.

No Python, o bloqueio persiste até o cursor ser liberado. Caso contrário, todos os outros aplicativos ou scripts poderão ser desnecessariamente impedidos de acessar um conjunto de dados. Um cursor pode ser liberado por um dos seguintes:

Incluindo o cursor dentro de uma instrução with, que garantirá a liberação de bloqueios, independentemente de o cursor ser ou não concluído com êxito;

Chamando reset () no cursor;

A conclusão do cursor;

Excluindo explicitamente o cursor usando a instrução del do Python - ESRI

Bloquear com cursores arcpy.da é praticamente o mesmo que bloquear com os cursores arcpy originais.

Após testar seu código, e como Gberard apontou, não há referência ao cursor após o término da compreensão.
Além disso, não há bloqueios na classe de recurso após o término da compreensão.

jbalk
fonte
1
Excluindo o que? Não há referência ao objeto cursor após o término da compreensão; portanto, em teoria, ele deve ser fechado. Se a implementação da ESRI se comporta ou não como você espera é outra questão, e não acho que os documentos realmente respondam a isso.
Mikewatt 12/1218