¿Eliminar el cursor utilizado en SearchCursor dentro de la comprensión del diccionario?

12

Si es mejor abrir cursores con una instrucción with para asegurarse de que se elimine, así:

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

Luego, si se usa un cursor como iterable en una comprensión como esta:

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

¿Es necesario eliminar el cursor después de usarlo en la comprensión?

J. Flann
fuente
1
Gran pregunta ¿Estás tratando de manejar bloqueos de esquema? Hay algunas publicaciones tempranas (en su mayoría desactualizadas) sobre un tema similar, aunque no puedo encontrar una fuente definitiva sobre los nuevos dacursores: sgillies.net/2011/02/01/get-with-it.html y help.arcgis.com/ es / arcgisdesktop / 10,0 / ayuda / index.html # // ... . En particular, mire los comentarios de @JasonScheirer al final del primer enlace.
Aaron

Respuestas:

13

Si es absolutamente necesario es una pregunta incorrecta. La pregunta es si es una buena idea.

Como regla general en la programación, debe evitar hacer cosas raras y usar la mejor herramienta para el trabajo . Si algo tiene una forma explícita de liberar recursos, simplemente explique la versión y termine con ella:

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

Lo que quizás no sepa es que la withcláusula en realidad invoca lógica adicional. Una withcláusula requiere un administrador de contexto, que debe tener un método __enter__(invocado cuando se ingresa el bloque) y __exit__(invocado cuando se sale del bloque). En particular, el __exit__método se invoca independientemente de si se produjo una excepción, lo que garantiza que el programa siempre libera el recurso incluso en caso de error. Esto le proporciona a su código documentación explícita de cuándo se adquiere un recurso y cuándo se libera, y garantiza que se pueda liberar un recurso lo antes posible.

Por el contrario, en realidad no puede depender del tiempo de ejecución para cerrarlo mágicamente de inmediato. Esto se debe a que la forma en que se cierra es invocando el destructor del objeto, lo que puede suceder o no de inmediato. Python no ofrece ninguna garantía acerca de cuándo se invoca un destructor, solo que eventualmente lo será cuando se recolecte basura del objeto. (Ver aquí .) Actualmente, Python está implementado para que ocurra tan pronto como ya no haya una referencia a un objeto. Pero es fácil propagar accidentalmente referencias a un objeto, y el tiempo de ejecución de Python puede cambiar.

Considere también el mantenimiento a largo plazo. No hay ninguna referencia a largo plazo ahora, pero lo que sucede en 6 meses cuando es necesario modificar el código de modo que no es una referencia? ¿Qué pasa si alguien más lo hace? Es posible que la persona que realiza el cambio no piense cambiar a un withbloque ya que no hay uno ya allí. Haga que la limpieza de sus recursos sea un hábito y tendrá muchos menos problemas.

¿Realmente desea vincular su código a los detalles de implementación de la recolección de basura? ¿Desea tener que pensar constantemente si podría estar propagando accidentalmente una referencia a través de una excepción? No, tu no. Imagínese si eso sucedió cuando se invocó el script en ArcMap. El usuario se vería obligado a cerrar todo el proceso solo para liberar el archivo. Así que no te pongas en esa posición. Libere el recurso explícitamente. Guardar una línea de código no vale los riesgos de los problemas que puede causar. Los administradores de contexto son el mecanismo estándar para adquirir y liberar recursos en Python, y lo hacen muy bien.

La conclusión es que no liberarlo explícitamente es una mala idea.

Esto, por supuesto, supone que el código tiene alguna posibilidad de afectar a otra persona, como ponerlo en un script que alguien más necesitará ejecutar o mantener o podría retrasar la entrega de su trabajo si tiene que cerrar ArcMap por completo porque No puedo guardar tus cambios. Si eres el único que se verá afectado por un problema, entonces, por supuesto, vuela frente a las buenas prácticas todo lo que quieras.

jpmc26
fuente
3

No, no es necesario eliminar un cursordespués de usarlo en una comprensión. A cursores una instancia de una clase, que es un objeto (todo en python es un objeto). Cada sesión de Python tiene una namespaceque contiene referencias a todos los objetos de la sesión; piense en ello como un diccionario donde las claves son referencias a cada objeto, y los valores son los propios objetos. Cuando el 'recuento de referencia', el número de claves que se refieren a ese objeto, cae a cero, el objeto se elimina y la memoria se reasigna . Cuando usa a cursoren una comprensión, no hay referencia a ese objeto en el espacio de nombres. Una vez realizada la comprensión, el objeto se eliminará.

No hay entrada en el espacio de nombres y, por lo tanto, no es necesario eliminar nada. ESRI también ilustra esta sintaxis en el ejemplo 2, aquí .

Para aclarar aún más, si ejecuta:

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

Verá que aparece un archivo .lock en el directorio (consulte su explorador de archivos). La referencia al cursor es a, lo que hará que el cursor(y por lo tanto el bloqueo) persista hasta que ase elimine. Entonces cuando corres:

>>> del(a)

Se eliminará la entrada en el espacio de nombres y se liberará el bloqueo (el archivo .lock desaparecerá). Si tu corres:

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

No verá un archivo de bloqueo o desaparecerá cuando se complete el comando. Sin una entrada en el espacio de nombres, el cursorno es persistente. tse refiere a la lista que acaba de crear, no a la cursorutilizada para crearla.

Para resumir, solo debe preocuparse por eliminar cursorscuando tienen una referencia en el espacio de nombres (es decir, cuando los ha asignado a una variable, como aen el ejemplo anterior).

Chris
fuente
2
Esta es una práctica de programación extremadamente pobre. Si algo tiene una forma explícita de liberar recursos, lo usa .
jpmc26
@ jpmc26, ¿Qué parte es "práctica de programación extremadamente pobre"? ¿Comprensiones en general? ¿O solo si lo iterable se instancia dentro de la comprensión? Pensé que un argumento fuerte para este último es que libera inmediatamente el recurso.
Tom
@ Tom No está lanzando recursos explícitamente. Las comprensiones son herramientas fantásticas, y crear instancias de iterables normales dentro de ellas es completamente normal. Lo malo aquí es que los objetos del cursor adquieren bloqueos de archivos y no hay una liberación explícita de ellos. Vea mi respuesta para más detalles.
jpmc26
2

Los cursores de actualización e inserción no se pueden crear para una tabla o clase de entidad si existe un bloqueo exclusivo para ese conjunto de datos. Las funciones UpdateCursor o InsertCursor fallan debido a un bloqueo exclusivo en el conjunto de datos. Si estas funciones crean con éxito un cursor, aplican un bloqueo exclusivo en el conjunto de datos para que dos scripts no puedan crear una actualización o insertar el cursor en el mismo conjunto de datos.

En Python, el bloqueo persiste hasta que se suelta el cursor. De lo contrario, se podría evitar innecesariamente que todas las demás aplicaciones o scripts accedan a un conjunto de datos. Un cursor puede ser liberado por uno de los siguientes:

Incluyendo el cursor dentro de una instrucción with, que garantizará la liberación de bloqueos independientemente de si el cursor se completó o no con éxito;

Llamando a reset () en el cursor;

La finalización del cursor;

Eliminar explícitamente el cursor usando la declaración del de Python - ESRI

Bloquear con los cursores arcpy.da es casi lo mismo que bloquear con los cursores originales arcpy.

Después de probar su código, y como señaló gberard, no hay referencia al cursor una vez que finaliza la comprensión.
Además, no hay bloqueos en la clase de entidad después de que finaliza la comprensión.

jbalk
fuente
1
¿Eliminar qué? No hay referencia al objeto cursor después de que finaliza la comprensión, por lo que en teoría debería cerrarse. Si la implementación de ESRI se comporta o no como es de esperar es otra pregunta, y no creo que los documentos realmente respondan eso.
mikewatt