Suppression du curseur utilisé dans SearchCursor dans la compréhension du dictionnaire?

12

S'il est préférable d'ouvrir les curseurs à l'aide d'une instruction with pour vous assurer qu'il est supprimé, comme ceci:

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

Ensuite, si un curseur est utilisé comme itérable dans une compréhension comme celle-ci:

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

Faut-il supprimer le curseur après l'avoir utilisé dans la compréhension?

J. Flann
la source
1
Grande question. Essayez-vous de gérer les verrous de schéma? Il y a quelques premiers messages (pour la plupart obsolètes) sur un sujet similaire, bien que je ne trouve pas de source définitive sur les nouveaux dacurseurs: sgillies.net/2011/02/01/get-with-it.html et help.arcgis.com/ fr / arcgisdesktop / 10.0 / help / index.html # //… . En particulier, regardez les commentaires de @JasonScheirer au bas du premier lien.
Aaron

Réponses:

13

Que ce soit absolument nécessaire n'est pas la bonne question à poser. La question est de savoir si c'est une bonne idée.

En règle générale dans la programmation, vous devez éviter de faire des choses étranges et utiliser le meilleur outil pour le travail . Si quelque chose a un moyen explicite de libérer des ressources, il suffit de rendre la version explicite et d'en finir avec:

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

Ce que vous ignorez peut-être, c'est que la withclause invoque en fait une logique supplémentaire. Une withclause nécessite un gestionnaire de contexte, qui doit avoir une méthode __enter__(invoquée lorsque le bloc est entré) et __exit__(invoquée lorsque le bloc est quitté). En particulier, la __exit__méthode est invoquée indépendamment du fait qu'une exception s'est produite, garantissant que le programme libère toujours la ressource même en cas d'erreur. Cela donne à votre code une documentation explicite sur le moment où une ressource est acquise et quand elle est libérée, et cela garantit qu'une ressource peut être libérée dès que possible.

En revanche, vous ne pouvez pas réellement dépendre du runtime pour le fermer comme par magie immédiatement pour vous. Cela est dû au fait qu'il se ferme en invoquant le destructeur de l'objet, ce qui peut ou non se produire immédiatement. Python ne fait aucune garantie quant à l'invocation d'un destructeur, mais seulement que ce sera finalement lorsque l'objet sera récupéré. (Voir ici .) Actuellement, Python est implémenté de sorte qu'il se produit dès qu'il n'y a plus de référence à un objet. Mais il est facile de propager accidentellement des références à un objet, et l'exécution de Python peut changer.

Pensez également à l'entretien à long terme. Il n'y a pas de référence à long terme maintenant, mais que se passe-t-il en 6 mois lorsque vous devez modifier le code pour qu'il y ait une référence? Et si quelqu'un d'autre le fait? La personne qui effectue le changement peut ne pas penser à passer à un withbloc car il n'y en a pas déjà un. Faites du nettoyage de vos ressources une habitude et vous aurez beaucoup moins de problèmes avec cela.

Voulez-vous vraiment lier votre code aux détails d'implémentation de la récupération de place? Voulez-vous avoir constamment à réfléchir à la possibilité de propager accidentellement une référence via une exception? Non, non. Imaginez si cela s'est produit lorsque le script a été invoqué dans ArcMap. L'utilisateur serait obligé de fermer l'ensemble du processus juste pour libérer le fichier. Alors ne vous mettez pas dans cette position. Libérez la ressource explicitement. L'enregistrement d'une ligne de code ne vaut pas les risques de problèmes qu'elle peut causer. Les gestionnaires de contexte sont le mécanisme standard pour acquérir et libérer des ressources en Python, et ils le font très bien.

L'essentiel est que ne pas le publier explicitement est une mauvaise idée.

Bien entendu, cela suppose que le code a une certaine possibilité d'affecter quelqu'un d'autre, comme le mettre dans un script que quelqu'un d'autre devra exécuter ou maintenir ou cela pourrait retarder la livraison de votre travail si vous devez fermer ArcMap complètement parce que vous ne peut pas enregistrer vos modifications. Si vous êtes le seul à être touché par un problème, alors par tous les moyens, allez à l'encontre des bonnes pratiques tout ce que vous voulez.

jpmc26
la source
3

Non, il n'est pas nécessaire de supprimer un cursoraprès l'avoir utilisé dans une compréhension. A cursorest une instance d'une classe, qui est un objet (tout en python est un objet). Chaque session python a un namespacequi contient des références à tous les objets de la session - pensez-y comme un dictionnaire où les clés sont des références à chaque objet, et les valeurs sont les objets eux-mêmes. Lorsque le «nombre de références» - le nombre de clés qui se réfèrent à cet objet - tombe à zéro, l'objet est supprimé et la mémoire réallouée . Lorsque vous utilisez un cursordans une compréhension, il n'y a aucune référence à cet objet dans l'espace de noms. Une fois la compréhension terminée, l'objet sera supprimé.

Il n'y a aucune entrée dans l'espace de noms, et donc pas besoin de supprimer quoi que ce soit. ESRI illustre également cette syntaxe dans l' exemple 2, ici .

Pour clarifier davantage, si vous exécutez:

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

Vous verrez un fichier .lock apparaître dans le répertoire (vérifiez votre explorateur de fichiers). La référence au curseur est a, ce qui rendra le cursor(et donc le verrou) persistant jusqu'à ce qu'il asoit supprimé. Ainsi, lorsque vous exécutez ensuite:

>>> del(a)

L'entrée dans l'espace de noms sera supprimée et le verrou sera libéré (le fichier .lock disparaîtra). Si vous exécutez:

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

Soit vous ne verrez pas de fichier de verrouillage, soit il disparaîtra à la fin de la commande. Sans entrée dans l'espace de noms, le cursorn'est pas persistant. tfait référence à la liste que vous venez de créer, pas à celle cursorutilisée pour la créer.

Pour résumer, vous n'avez qu'à vous soucier de les supprimer cursorslorsqu'ils ont une référence dans l'espace de noms (c'est-à-dire lorsque vous les avez affectés à une variable, comme adans l'exemple ci-dessus).

Chris
la source
2
Il s'agit d' une pratique de programmation extrêmement médiocre. Si quelque chose a un moyen explicite de libérer des ressources, vous l'utilisez .
jpmc26
@ jpmc26, Quelle partie est "une pratique de programmation extrêmement mauvaise"? Compréhensions en général? Ou seulement si l'itérable est instancié dans la compréhension? Je pensais que l'un des principaux arguments en faveur de ce dernier est qu'il libère immédiatement la ressource.
Tom
@Tom Ne pas libérer explicitement les ressources. Les compréhensions sont des outils fantastiques et il est tout à fait normal d'instancier des itérables normaux à l'intérieur. Ce qui est mauvais ici, c'est que les objets curseur acquièrent des verrous de fichiers et il n'y a pas de libération explicite d'entre eux. Voir ma réponse pour plus de détails.
jpmc26
2

Les curseurs de mise à jour et d'insertion ne peuvent pas être créés pour une table ou une classe d'entités s'il existe un verrou exclusif pour cet ensemble de données. Les fonctions UpdateCursor ou InsertCursor échouent en raison d'un verrou exclusif sur l'ensemble de données. Si ces fonctions réussissent à créer un curseur, elles appliquent un verrou exclusif sur l'ensemble de données afin que deux scripts ne puissent pas créer une mise à jour ou insérer un curseur sur le même ensemble de données.

En Python, le verrou persiste jusqu'à ce que le curseur soit relâché. Sinon, toutes les autres applications ou scripts pourraient être inutilement empêchés d'accéder à un ensemble de données. Un curseur peut être libéré par l'un des éléments suivants:

Inclure le curseur dans une instruction with, ce qui garantira la libération des verrous, que le curseur soit ou non terminé avec succès;

Appel de reset () sur le curseur;

L'achèvement du curseur;

Suppression explicite du curseur à l'aide de l'instruction del de Python - ESRI

Le verrouillage avec les curseurs arcpy.da est à peu près le même que le verrouillage avec les curseurs arcpy d'origine.

Après avoir testé votre code, et comme l'a souligné gberard, il n'y a aucune référence au curseur après la fin de la compréhension.
De plus, il n'y a aucun verrou sur la classe d'entités après la fin de la compréhension.

jbalk
la source
1
Supprimer quoi? Il n'y a aucune référence à l'objet curseur après la fin de la compréhension, donc en théorie, il devrait se fermer. Que l'implémentation d'ESRI se comporte ou non comme vous vous y attendez est une autre question, et je ne pense pas que les docs y répondent vraiment.
mikewatt