RAII en Python - automatique de la destruction au moment de quitter un champ d'application
J'ai essayé de trouver RAII en Python.
L'Allocation des ressources Est d'Initialisation est un modèle en C++ par lequel
un objet est initialisé comme il est créé. Si elle échoue, alors il jette
une exception. De cette façon, le programmeur sait qu'
l'objet ne sera jamais laissé dans un demi-construit de l'état. Python
peuvent le faire beaucoup plus.
Mais RAII travaille également avec les règles de portée de C++
pour assurer la destruction rapide de l'objet. Dès que la variable
pop hors de la pile, il est détruit. Cela peut se produire en Python, mais seulement
si il n'y a aucune externes ou des références circulaires.
Plus important encore, un nom pour un objet existe toujours jusqu'à ce que la fonction qu'il est
en sorties (et parfois plus). Les Variables au niveau du module sera
de rester pour la durée de vie du module.
Je voudrais avoir une erreur si je fais quelque chose comme ceci:
for x in some_list:
...
... 100 lines later ...
for i in x:
# Oops! Forgot to define x first, but... where's my error?
...
J'ai pu supprimer manuellement les noms après je l'ai utilisé,
mais qui serait assez moche, et exigent un effort de ma part.
Et j'aimerais qu'il fasse Ce que je veux Dire dans ce cas:
for x in some_list:
surface = x.getSurface()
new_points = []
for x,y,z in surface.points:
... # Do something with the points
new_points.append( (x,y,z) )
surface.points = new_points
x.setSurface(surface)
Python n'certains de portée, mais pas au niveau d'indentation, juste à
le niveau fonctionnel. Il semble stupide d'exiger que je fais une nouvelle fonction
juste à portée des variables afin que je puisse réutiliser un nom.
Python 2.5 est la "avec la" déclaration de
mais cela nécessite que j'ai choisi de mettre en __enter__
et __exit__
fonctions
et semble généralement plus orienté vers le nettoyage de ressources comme des fichiers
mutex et de serrures quel que soit le vecteur de sortie. Il ne permet pas de portée.
Ou ai-je raté quelque chose?
J'ai cherché pour "Python RAII" et "Python portée" et je n'étais pas capable de trouver quelque chose qui
a abordé la question directement et de façon autoritaire.
J'ai regardé sur tous les PEPs. Le concept ne semble pas être traitées
dans Python.
Suis-je une mauvaise personne parce que je veux avoir la portée des variables en Python?
Est-ce tout simplement trop onu-Pythonic?
Je ne suis pas grokking il?
Peut-être que je suis en train de prendre de suite les avantages de la dynamique des aspects de la langue.
Est-il égoïste parfois envie champ d'application forcée?
Je suis paresseux pour vouloir le compilateur/interpréteur
pour attraper ma négligence variable réutilisation des erreurs? Eh bien, oui, bien sûr, je suis paresseux,
mais je suis paresseux dans le mauvais sens?
- RAII est l'équivalent en Python de
with
déclaration (en fait, je suis surpris de voir que l'acronyme de ne pas le faire dans la PPE 343 - c'était certainement jeté autour d'un lot dans les discussions). L'élément manquant que vous demandez est anonyme, les règles de portée à partir de C/C++ et non, Python n'a pas de celles (même si vous pouvez trouver le report de la PEP 3150 d'intérêt). C'est complètement indépendant de la RAII question. - Merci pour la réponse. Je pensais que PEP 3150 était de répondre à ma question, mais j'ai réalisé qu'il ne dit rien de la suppression de l'espace de noms. C'est purement pour déplacer l'appel de la fonction ci-dessus le code qui obtient des valeurs pour les paramètres de sorte qu'il est plus évident que le code est en train d'accomplir. Ce que je n'aime pas à propos de
with
est que le nom ne va pas loin, et que je dois passer le code en__exit__
au lieu de__del__
. Il est peut-être un habile objet wrapper qui seraitdel
le nom de l'objet et de ne pas exiger que je code un__exit__
fonction? Idéalement, il serait affirmer que l'arbitre était le nombre de 0. - même si le refcount > 0, vous pouvez
del
le nom de l'objet à partir de votre propre champ d'application et de se débarrasser de ce nom - ce qui est totalement distincte de en fait en détruisant l'objet. Par exemple, si un objet a été inséré dans un conteneur, son refcount serait > 0, mais vous pouvez toujoursdel
le nom que vous avez utilisé pour faire référence à l'objet lui-même. Peut-être que c'est une partie de votre confusion -del
supprime le nom, pas l'objet sous-jacent. C'est une distinction en Python qu'en C/C++ les gens luttent avec - vous traitez avec des noms et des liaisons de noms, pas les variables/les références qui sont alloc ed et libéré.
Vous devez vous connecter pour publier un commentaire.
tl;dr RAII n'est pas possible, vous mélangez-les avec la portée, en général, et si vous manquez une des plus étendues que vous êtes probablement écrire du mauvais code.
Peut-être que je ne comprends pas votre question(s), ou vous n'avez pas obtenir des choses essentielles à propos de Python... tout d'Abord, déterministe de la destruction des objets liés à portée impossible dans une de ces ordures de la langue. Les Variables en Python sont simplement des références. Vous ne voulez pas un
malloc
" d partie de la mémoire àfree
'd dès qu'un pointeur vers celui-ci est hors de portée, le feriez-vous? Pratique exception dans certains circonstances, si vous arrive d'utilisation ref compter - mais aucune langue n'est assez fou pour définir l'exacte mise en œuvre dans la pierre.Et même si vous avez de comptage de référence, comme dans Disponible, c'est un détail d'implémentation. En général, y compris en Python qui a diverses implémentations pas à l'aide de ref de comptage, vous devriez code comme si chaque objet se bloque jusqu'à ce mémoire.
Comme pour les noms existants pour le reste de l'invocation de la fonction: Vous peut supprimer un nom de la actuelles ou étendue globale via le
del
déclaration. Toutefois, cela n'a rien à voir avec le manuel de gestion de la mémoire. Il supprime juste la référence. Qui peut ou peut ne pas arriver à déclencher l'objet référencé à GC avais et n'est pas le point de l'exercice.Vous sont corrects,
with
n'a rien à voir avec la portée, juste avec le nettoyage déterministe (de sorte qu'il chevauche le RAII dans les extrémités, mais pas dans les moyens).Pas. Décent portée lexicale est un mérite indépendante de la dynamique-/staticness. Certes, Python (2 - 3 assez bien fixé ce) a des faiblesses à cet égard, même s'ils sont plus dans le domaine de la fermeture.
Mais pour expliquer "pourquoi": Python doit être prudent avec où il commence un nouveau champ d'application en raison sans déclaration disent le contraire, la cession d'un nom en fait un local à l'intime/champ d'application actuel. Donc par exemple, si une boucle for a son propre champ d'application, vous ne pouvez pas facilement modifier les variables en dehors de la boucle.
Encore une fois, j'imagine que accidentel resuse d'un nom (de façon à y introduire des erreurs ou des pièges) est rare et un petit de toute façon.
Edit: À l'état de ce nouveau aussi clairement que possible:
with
déclaration. Oui, il n'a pas d'introduire un nouveau champ d'application (voir ci-dessous), parce que ce n'est pas ce qu'il est. Il n'a pas d'importance, il supprime le nom de l'objet géré est lié n'est pas supprimé - le nettoyage est arrivé néanmoins, ce qui reste est un "ne me touchez pas, je suis inutilisable" de l'objet (par exemple un fichier de flux).with
est pour. Parce que les ressources de nettoyage devrait être dans__exit__
ou, si vous êtes une pauvre âme qui n'a jamais entendu parler de contexte, les gestionnaires, dans certains extérieur gérable de ressources. GC est assez bien, par définition, non déterministe, et vous ne serez pas obtenir Python pour effectuer un visage talon tour et re-introduire le manuel de gestion de la mémoire ou de certains légèrement déchargée variante de celui-ci. Travailler avec le langage tel qu'il est ou en utiliser un autre. Que penseriez-vous au sujet des demandes de GC en C++?__exit__
est un mauvais remplacement, car je veux le nom s'en aller sans undel
, et__exit__
n'est pas appelé à l'exception dewith
. Il pourrait être beaucoup plus simple.with
est la voie à suivre pour le nettoyage déterministe. Et bien que le nom reste après lawith
bloc terminé, le nettoyage est effectué et ainsi, vous obtenez le même effet appliqué RAII, mais par d'autres moyens. Ou avez-vous une utilisation abusive du terme RAII et en fait juste voulez plus fine de portée?with
mais après la première émotion que j'ai réalisé que ce n'était pas ce que je cherchais. Je suis résigné à Python ayant pas appropriée basée sur la pile de portée alternative (qui inclut le nom). Peut-être que je devrais supprimer RAII de ma question, mais je pense qu'il est une partie intégrante du concept à la question de l'étendue du nom. La question a deux exemples qui tous deux illustrent la question du nom de la variable ne va pas loin. Merci pour l'aide, cependant. Au moins maintenant je sais que je ne suis pas rater quelque chose d'évident.with
et ce n'est pas ce que je veux, mais je suppose que c'est le "Pythonic way", qui est une partie de ma question. BTW, ton dernier point semble être contredite par le générateur de champ d'application, et les interprétations de la liste ont leur propre champ d'application en Python 3000 (voir les autres réponses). Les choses semblent être la tendance de mon chemin. Cette invalide également votre premier point, que je ne demande pas la pile mise en œuvre, juste comportement. Disons juste d'accord que je suis coincé dans le C++ état d'esprit et je veux des noms d'aller hors de portée et Python ne pas me rencontrer encore là. Je suis unPythonic. 🙁__del__
documentation] mentionne, (1)del
ne garantit pas du tout que__del__
est appelé (si il y a d'autres références), et (2) les références circulaires ne sont pas nettoyés par des compteurs refcount seul.Vous avez raison sur
with
- il est totalement sans rapport avec la variable de portée.Éviter les variables globales si vous pensez qu'ils sont un problème. Cela inclut les variables de niveau module.
Le principal outil pour cacher l'état en Python sont des classes.
Générateur d'expressions (et en Python 3 interprétations de la liste) ont leur propre champ d'application.
Si vos fonctions sont suffisamment longtemps pour vous de perdre la trace des variables locales, vous devriez probablement refactoriser le code.
Cela est considéré comme sans importance dans la résolution GC langues, qui sont fondés sur l'idée que la mémoire est fongibles. Il n'y a pas de besoin urgent de récupérer la mémoire d'un objet tant qu'il y aura assez de mémoire d'ailleurs d'attribuer de nouveaux objets. Non fongible des ressources comme des descripteurs de fichiers, sockets, et les mutex sont considérés comme un cas particulier d'être traitée spécialement (par exemple,
with
). Cela contraste avec le C++de modèle qui traite de toutes les ressources de même.Python n'a pas de pile variables. En C++ termes, tout est un
shared_ptr
.Il aussi ne la portée de l'alternateur, de son degré de compréhension (et en 3.x, dans tous interprétations).
Si vous ne voulez pas tabasser votre
for
les variables de boucle, ne pas utiliser autant defor
boucles. En particulier, c'est l'onu-Pythonic à utiliserappend
dans une boucle. Au lieu de:écrire:
ou
Fondamentalement, vous utilisez probablement une mauvaise langue. Si vous voulez sane règles de portée et fiable destruction puis coller avec C++ ou essayez de Perl. Le GC débat sur quand la mémoire est libérée semble manquer le point. C'est sur la libération d'autres ressources comme les mutex et les descripteurs de fichiers. Je crois que C# fait la distinction entre un destructeur est appelé lorsque le compteur de référence est mis à zéro et quand il décide de recycler la mémoire. Les gens ne sont pas préoccupés par le recyclage de la mémoire mais ne veulent savoir dès qu'il n'est plus référencé. C'est dommage que Python a un réel potentiel en tant que langue. Mais il est non conventionnelles de délimitation de l'étendue et peu fiables destructeurs (ou au moins la mise en œuvre dépendantes) signifie que l'on est refusé à la puissance que vous obtenez avec C++ et Perl.
Intéressant le commentaire fait à propos pour l'utilisation d'une nouvelle mémoire si elle est disponible, plutôt que le recyclage des vieux dans le GC. N'est-ce pas juste une façon élégante de dire qu'il perd de la mémoire 🙂
Lors du passage à Python après des années de C++, je trouve qu'il est tentant de s'appuyer sur
__del__
pour imiter RAII-type de comportement, par exemple, fermer des fichiers ou des connexions. Cependant, il existe des situations (par exemple, modèle observateur tel que mis en œuvre par Rx) où la chose observée maintient une référence vers l'objet, de le garder en vie! Donc, si vous voulez fermer la connexion, avant d'être arrêté par la source, vous n'obtiendrez pas n'importe où en essayant de le faire dans__del__
.La situation suivante se pose dans l'INTERFACE utilisateur de programmation:
Donc, voici la façon d'obtenir RAII-type de comportement: créer un récipient avec de l'ajouter et de supprimer les crochets:
Si
UiComponent.children
est unScopedList
, qui appelleacquire
etdispose
méthodes sur les enfants, vous obtenez la même garantie de déterministe de l'acquisition des ressources et de l'élimination que vous êtes habitué à en C++.