Je suis tombé sur un mur en important des modules dans un script Python. Je ferai de mon mieux pour décrire l'erreur, pourquoi je la rencontre et pourquoi j'attache cette approche particulière pour résoudre mon problème (que je décrirai dans une seconde):
Supposons que j'ai un module dans lequel j'ai défini des fonctions / classes utilitaires, qui se réfèrent aux entités définies dans l'espace de noms dans lequel ce module auxiliaire sera importé (soit "a" une telle entité):
module 1:
def f():
print a
Et puis j'ai le programme principal, où "a" est défini, dans lequel je veux importer ces utilitaires:
import module1
a=3
module1.f()
L'exécution du programme déclenchera l'erreur suivante:
Traceback (most recent call last):
File "Z:\Python\main.py", line 10, in <module>
module1.f()
File "Z:\Python\module1.py", line 3, in f
print a
NameError: global name 'a' is not defined
Des questions similaires ont été posées dans le passé (il y a deux jours, d'uh) et plusieurs solutions ont été suggérées, mais je ne pense pas vraiment qu'elles correspondent à mes besoins. Voici mon contexte particulier:
J'essaie de créer un programme Python qui se connecte à un serveur de base de données MySQL et affiche / modifie les données avec une interface graphique. Par souci de propreté, j'ai défini le tas de fonctions auxiliaires / utilitaires liées à MySQL dans un fichier séparé. Cependant, ils ont tous une variable commune, que j'avais initialement définie dans le module utilitaires, et qui est l' objet curseur du module MySQLdb. J'ai réalisé plus tard que l' objet curseur (qui est utilisé pour communiquer avec le serveur db) devait être défini dans le module principal, de sorte que le module principal et tout ce qui y est importé puisse accéder à cet objet.
Le résultat final serait quelque chose comme ceci:
utilities_module.py:
def utility_1(args):
code which references a variable named "cur"
def utility_n(args):
etcetera
Et mon module principal:
program.py:
import MySQLdb, Tkinter
db=MySQLdb.connect(#blahblah) ; cur=db.cursor() #cur is defined!
from utilities_module import *
Et puis, dès que j'essaye d'appeler l'une des fonctions utilitaires, cela déclenche l'erreur "nom global non défini" susmentionnée.
Une suggestion particulière était d'avoir une instruction "from program import cur" dans le fichier utilitaires, comme ceci:
utilities_module.py:
from program import cur
#rest of function definitions
program.py:
import Tkinter, MySQLdb
db=MySQLdb.connect(#blahblah) ; cur=db.cursor() #cur is defined!
from utilities_module import *
Mais c'est une importation cyclique ou quelque chose comme ça et, en bout de ligne, ça plante aussi. Ma question est donc:
Comment diable puis-je rendre l'objet "cur", défini dans le module principal, visible par les fonctions auxiliaires qui y sont importées?
Merci pour votre temps et mes plus sincères excuses si la solution a été publiée ailleurs. Je ne trouve tout simplement pas la réponse moi-même et je n'ai plus d'astuces dans mon livre.
fetch_all
parcourir deux listes à la place. , ou simplement pour que vous puissiez avoir deux threads / greenlets / callback-chains / tout ce qui utilise la base de données sans conflits).db
(etcur
, si vous insistez) dans un module séparé qui les deuxprogram
et de l'utilities_module
importer. De cette façon, vous n'obtenez pas de dépendances circulaires (importation de programme à partir de modules importés par le programme) et la confusion qui les accompagne.Réponses:
Les globaux en Python sont globaux à un module , pas à tous les modules. (Beaucoup de gens sont déroutés par cela, car dans, par exemple, C, un global est le même dans tous les fichiers d'implémentation, sauf si vous le faites explicitement
static
.)Il existe différentes façons de résoudre ce problème, en fonction de votre cas d'utilisation réel.
Avant même de s'engager dans cette voie, demandez-vous si cela doit vraiment être mondial. Peut-être voulez-vous vraiment une classe, avec
f
comme méthode d'instance, plutôt qu'une simple fonction gratuite? Ensuite, vous pouvez faire quelque chose comme ceci:Si vous voulez vraiment un global, mais qu'il est juste là pour être utilisé par
module1
, placez-le dans ce module.D'un autre côté, s'il
a
est partagé par un grand nombre de modules, placez-le ailleurs et demandez à tout le monde de l'importer:… Et, dans module1.py:
Ne pas utiliser une
from
importation à moins que la variable est destinée à être une constante.from shared_stuff import a
créerait une nouvellea
variable initialisée à tout ce qui étaitshared_stuff.a
référencé au moment de l'importation, et cette nouvellea
variable ne serait pas affectée par les affectations àshared_stuff.a
.Ou, dans le cas rare où vous en avez vraiment besoin pour être vraiment global partout, comme un intégré, ajoutez-le au module intégré. Les détails exacts diffèrent entre Python 2.x et 3.x. Dans 3.x, cela fonctionne comme ceci:
la source
Pour contourner ce problème, vous pouvez envisager de définir des variables d'environnement dans la couche externe, comme ceci.
main.py:
mymodule.py:
Par précaution supplémentaire, traitez le cas où MYVAL n'est pas défini à l'intérieur du module.
la source
Une fonction utilise les globaux du module dans lequel elle est définie . Au lieu de définir
a = 3
, par exemple, vous devriez définirmodule1.a = 3
. Donc, si vous voulez êtrecur
disponible en tant que global dansutilities_module
, définissezutilities_module.cur
.Une meilleure solution: n'utilisez pas de globaux. Transmettez les variables dont vous avez besoin aux fonctions qui en ont besoin, ou créez une classe pour regrouper toutes les données et transmettez-la lors de l'initialisation de l'instance.
la source
Cet article n'est qu'une observation pour le comportement de Python que j'ai rencontré. Peut-être que les conseils que vous avez lus ci-dessus ne fonctionnent pas pour vous si vous avez fait la même chose que moi ci-dessous.
À savoir, j'ai un module qui contient des variables globales / partagées (comme suggéré ci-dessus):
Ensuite, j'ai eu le module principal qui importe le contenu partagé avec:
et quelques autres modules qui ont en fait peuplé ces tableaux. Ceux-ci sont appelés par le module principal. En quittant ces autres modules, je peux clairement voir que les tableaux sont remplis. Mais en les relisant dans le module principal, ils étaient vides. C'était plutôt étrange pour moi (enfin, je suis nouveau sur Python). Cependant, lorsque je change la façon dont j'importe le sharedstuff.py dans le module principal pour:
cela a fonctionné (les tableaux étaient remplis).
Dis juste
la source
La solution la plus simple à ce problème particulier aurait été d'ajouter une autre fonction au sein du module qui aurait stocké le curseur dans une variable globale du module. Ensuite, toutes les autres fonctions pourraient également l'utiliser.
module 1:
programme principal:
la source
Étant donné que les globaux sont spécifiques au module, vous pouvez ajouter la fonction suivante à tous les modules importés, puis l'utiliser pour:
Ensuite, tout ce dont vous avez besoin pour transmettre les globaux actuels est:
la source
Comme je ne l'ai pas vu dans les réponses ci-dessus, j'ai pensé ajouter ma solution de contournement simple, qui consiste simplement à ajouter un
global_dict
argument à la fonction nécessitant les globaux du module appelant, puis à passer le dict dans la fonction lors de l'appel; par exemple:la source
La manière OOP de faire cela serait de faire de votre module une classe au lieu d'un ensemble de méthodes indépendantes. Ensuite, vous pouvez utiliser
__init__
ou une méthode setter pour définir les variables de l'appelant à utiliser dans les méthodes du module.la source