Vous utilisez from bar import a. adevient un symbole dans la portée globale du module d'importation (ou quelle que soit la portée dans laquelle l'instruction d'importation se produit).
Lorsque vous attribuez une nouvelle valeur à a, vous modifiez simplement la valeur qui apointe également, pas la valeur réelle. Essayez d'importer bar.pydirectement avec import barin __init__.pyet effectuez votre expérience là-bas en définissant bar.a = 1. De cette façon, vous modifierez bar.__dict__['a']réellement la valeur «réelle» de adans ce contexte.
C'est un peu compliqué avec trois couches mais bar.a = 1change la valeur de adans le module appelé barqui est en fait dérivé __init__.py. Cela ne change pas la valeur de ce aqui foobarvoit car il foobarvit dans le fichier réel bar.py. Vous pouvez définir bar.bar.asi vous souhaitez changer cela.
C'est l'un des dangers de l'utilisation de la from foo import barforme de l' importinstruction: elle se divise baren deux symboles, l'un visible globalement de l'intérieur fooduquel commence par pointer vers la valeur d'origine et un autre symbole visible dans la portée où l' importinstruction est exécutée. La modification d'un point de repère d'un symbole ne modifie pas non plus la valeur sur laquelle il pointe.
Ce genre de truc est un tueur lorsque vous essayez reloadun module de l'interpréteur interactif.
Merci! Votre réponse m'a probablement évité plusieurs heures de recherche du coupable.
jnns le
Il semble que même l' bar.bar.aapproche n'aidera pas beaucoup dans la plupart des cas d'utilisation. C'est probablement une idée faible de mélanger les affectations de compilation ou de module et d'exécution, car vous finirez par vous confondre (ou par d'autres qui utilisent votre code). Surtout dans les langages qui se comportent de manière quelque peu incohérente à ce sujet, comme le fait sans doute python.
matanster le
26
Une source de difficulté à cette question est que vous avez un programme nommé bar/bar.py: les import barimportations soit bar/__init__.pyou bar/bar.py, selon l'endroit où il est fait, ce qui le rend un peu lourd à suivre ce qui aest bar.a.
Voici comment cela fonctionne:
La clé pour comprendre ce qui se passe est de réaliser que dans votre __init__.py,
from bar import a
en effet fait quelque chose comme
a = bar.a
# … where bar = bar/bar.py (as if bar were imported locally from __init__.py)
et définit une nouvelle variable ( bar/__init__.py:asi vous le souhaitez). Ainsi, votre from bar import ain __init__.pylie le nom bar/__init__.py:aà l' bar.py:aobjet d' origine ( None). C'est pourquoi vous pouvez le faire from bar import a as a2dans __init__.py: dans ce cas, il est clair que vous avez les deux bar/bar.py:aet un nom de variable distinctbar/__init__.py:a2 (dans votre cas, les noms des deux variables se trouvent être les deux a, mais ils vivent toujours dans des espaces de noms différents: dans __init__.py, ils sont bar.aet a).
Maintenant, quand tu fais
import bar
print bar.a
vous accédez à la variable bar/__init__.py:a(depuis import barimporte votre bar/__init__.py). C'est la variable que vous modifiez (à 1). Vous ne touchez pas au contenu de la variable bar/bar.py:a. Alors quand tu fais par la suite
bar.foobar()
vous appelez bar/bar.py:foobar(), qui accède à la variable à apartir de bar/bar.py, qui est toujours None(quand foobar()est défini, il lie les noms de variable une fois pour toutes, donc le ain bar.pyn'est bar.py:apas une autre avariable définie dans un autre module - car il peut y avoir de nombreuses avariables dans tous les modules importés ). D'où la dernière Nonesortie.
Conclusion: il est préférable d'éviter toute ambiguïté import bar, par n'avoir aucun module (depuis répertoire marques un paquet déjà, que vous pouvez également importer avec ).bar/bar.pybar.__init__.pybar/import bar
En d'autres termes: il s'avère que cette idée fausse est très facile à faire.
Il est défini sournoisement dans la référence du langage Python: l'utilisation de l' objet au lieu du symbole . Je suggérerais que la référence du langage Python rende cela plus clair et moins épars.
Le fromformulaire ne lie pas le nom du module: il parcourt la liste des identifiants, recherche chacun d'eux dans le module trouvé à l'étape (1), et lie le nom dans l'espace de noms local à l' objet ainsi trouvé.
TOUTEFOIS:
Lorsque vous importez, vous importez la valeur actuelle du symbole importé et l'ajoutez à votre espace de noms tel que défini. Vous n'importez pas de référence, vous importez effectivement une valeur.
Ainsi, pour obtenir la valeur mise à jour de i, vous devez importer une variable qui contient une référence à ce symbole.
En d'autres termes, l'importation n'est PAS comme une déclaration importen JAVA, une externaldéclaration en C / C ++ ou même une useclause en PERL.
Au contraire, l'instruction suivante en Python:
from some_other_module import a as x
ressemble plus au code suivant dans K&R C:
extern int a;/*importfrom the EXTERN file */
int x = a;
(mise en garde: dans le cas Python, "a" et "x" sont essentiellement une référence à la valeur réelle: vous ne copiez pas l'INT, vous copiez l'adresse de référence)
En fait, je trouve que la méthode Python est importbeaucoup plus propre que celle de Java, car les espaces de noms / portées doivent toujours être correctement séparés et ne jamais interférer les uns avec les autres de manière inattendue. Comme ici: Modifier une liaison d'un objet à un nom dans un espace de noms (lire: associer quelque chose à la propriété globale d'un module) n'affecte jamais les autres espaces de noms (lire: la référence qui a été importée) en Python. Mais il le fait en Java, etc. En Python, vous avez seulement besoin de comprendre ce qui est importé, tandis qu'en Java, vous devez également comprendre l'autre module au cas où il modifierait cette valeur importée plus tard.
Tino
2
Je dois être en désaccord assez énergique. L'importation / l'inclusion / l'utilisation a une longue histoire d'utilisation du formulaire de référence et non du formulaire de valeur dans toutes les autres langues.
Mark Gerolimatos
4
Bon sang, j'ai frappé retour… il y a une attente d'importation par référence (selon @OP). En fait, un nouveau programmeur Python, aussi expérimenté soit-il, doit se faire dire cela de la manière «regarder». Cela ne devrait jamais arriver: sur la base d'un usage courant, Python a pris le mauvais chemin. Créez une "valeur d'importation" si nécessaire, mais ne confondez pas les symboles avec les valeurs au moment de l'importation.
bar.bar.a
approche n'aidera pas beaucoup dans la plupart des cas d'utilisation. C'est probablement une idée faible de mélanger les affectations de compilation ou de module et d'exécution, car vous finirez par vous confondre (ou par d'autres qui utilisent votre code). Surtout dans les langages qui se comportent de manière quelque peu incohérente à ce sujet, comme le fait sans doute python.Une source de difficulté à cette question est que vous avez un programme nommé
bar/bar.py
: lesimport bar
importations soitbar/__init__.py
oubar/bar.py
, selon l'endroit où il est fait, ce qui le rend un peu lourd à suivre ce quia
estbar.a
.Voici comment cela fonctionne:
La clé pour comprendre ce qui se passe est de réaliser que dans votre
__init__.py
,en effet fait quelque chose comme
et définit une nouvelle variable (
bar/__init__.py:a
si vous le souhaitez). Ainsi, votrefrom bar import a
in__init__.py
lie le nombar/__init__.py:a
à l'bar.py:a
objet d' origine (None
). C'est pourquoi vous pouvez le fairefrom bar import a as a2
dans__init__.py
: dans ce cas, il est clair que vous avez les deuxbar/bar.py:a
et un nom de variable distinctbar/__init__.py:a2
(dans votre cas, les noms des deux variables se trouvent être les deuxa
, mais ils vivent toujours dans des espaces de noms différents: dans__init__.py
, ils sontbar.a
eta
).Maintenant, quand tu fais
vous accédez à la variable
bar/__init__.py:a
(depuisimport bar
importe votrebar/__init__.py
). C'est la variable que vous modifiez (à 1). Vous ne touchez pas au contenu de la variablebar/bar.py:a
. Alors quand tu fais par la suitevous appelez
bar/bar.py:foobar()
, qui accède à la variable àa
partir debar/bar.py
, qui est toujoursNone
(quandfoobar()
est défini, il lie les noms de variable une fois pour toutes, donc lea
inbar.py
n'estbar.py:a
pas une autrea
variable définie dans un autre module - car il peut y avoir de nombreusesa
variables dans tous les modules importés ). D'où la dernièreNone
sortie.Conclusion: il est préférable d'éviter toute ambiguïté
import bar
, par n'avoir aucun module (depuis répertoire marques un paquet déjà, que vous pouvez également importer avec ).bar/bar.py
bar.__init__.py
bar/
import bar
la source
En d'autres termes: il s'avère que cette idée fausse est très facile à faire. Il est défini sournoisement dans la référence du langage Python: l'utilisation de l' objet au lieu du symbole . Je suggérerais que la référence du langage Python rende cela plus clair et moins épars.
TOUTEFOIS:
Lorsque vous importez, vous importez la valeur actuelle du symbole importé et l'ajoutez à votre espace de noms tel que défini. Vous n'importez pas de référence, vous importez effectivement une valeur.
Ainsi, pour obtenir la valeur mise à jour de
i
, vous devez importer une variable qui contient une référence à ce symbole.En d'autres termes, l'importation n'est PAS comme une déclaration
import
en JAVA, uneexternal
déclaration en C / C ++ ou même uneuse
clause en PERL.Au contraire, l'instruction suivante en Python:
ressemble plus au code suivant dans K&R C:
(mise en garde: dans le cas Python, "a" et "x" sont essentiellement une référence à la valeur réelle: vous ne copiez pas l'INT, vous copiez l'adresse de référence)
la source
import
beaucoup plus propre que celle de Java, car les espaces de noms / portées doivent toujours être correctement séparés et ne jamais interférer les uns avec les autres de manière inattendue. Comme ici: Modifier une liaison d'un objet à un nom dans un espace de noms (lire: associer quelque chose à la propriété globale d'un module) n'affecte jamais les autres espaces de noms (lire: la référence qui a été importée) en Python. Mais il le fait en Java, etc. En Python, vous avez seulement besoin de comprendre ce qui est importé, tandis qu'en Java, vous devez également comprendre l'autre module au cas où il modifierait cette valeur importée plus tard.