159 votes

Visibilité des variables globales dans les modules importés

Je me suis heurté à un mur en important des modules dans un script Python. Je vais faire de mon mieux pour décrire l'erreur, pourquoi je la rencontre, et pourquoi j'utilise cette approche particulière pour résoudre mon problème (que je vais décrire dans une seconde) :

Supposons que j'ai un module dans lequel j'ai défini certaines fonctions/classes utilitaires, qui font référence à des entités définies dans l'espace de noms dans lequel ce module auxiliaire sera importé (laissons "a" être une telle entité) :

module1 :

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 qu'elles correspondent vraiment à mes besoins. Voici mon contexte particulier :

J'essaie de faire un programme Python qui se connecte à un serveur de base de données MySQL et affiche/modifie les données avec une interface graphique. Pour des raisons de propreté, j'ai défini le groupe de fonctions auxiliaires/utilitaires liées à MySQL dans un fichier séparé. Cependant, elles ont toutes une variable commune, que j'avais initialement définie comme suit à l'intérieur de le module utilitaire, et qui est le curseur du module MySQLdb. J'ai réalisé par la suite que l'objet curseur (qui est utilisé pour communiquer avec le serveur de données) doit être défini dans le module principal, afin que le module principal et tout ce qui est importé dans ce module puissent accéder à cet objet.

Le résultat final serait quelque chose comme ceci :

Utilitaires_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'essaie d'appeler l'une des fonctions utilitaires, cela déclenche l'erreur susmentionnée "nom global non défini".

Une suggestion particulière était d'avoir une déclaration "from program import cur" dans le fichier utilitaire, comme ceci :

Utilitaires_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 de l'importation cyclique ou quelque chose comme ça et, en fin de compte, ça se plante aussi. Donc ma question est :

Comment faire en sorte que l'objet "cur", défini dans le module principal, soit 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é postée ailleurs. Je n'arrive pas à trouver la réponse moi-même et je n'ai plus d'astuces dans mon livre.

314voto

abarnert Points 94246

Les globaux en Python sont globaux à un module mais pas dans tous les modules. (Beaucoup de personnes sont confuses à ce sujet, parce que, par exemple, en C, un global est le même dans tous les fichiers d'implémentation, à moins que vous ne le rendiez explicitement identique ). 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 vous engager dans cette voie, demandez-vous si cela doit vraiment être mondial. Peut-être voulez-vous vraiment une classe, avec f comme une méthode d'instance, plutôt qu'une simple fonction libre ? Vous pourriez alors faire quelque chose comme ceci :

import module1
thingy1 = module1.Thingy(a=3)
thingy1.f()

Si vous voulez vraiment un global, mais qu'il est juste là pour être utilisé par module1 il est placé dans ce module.

import module1
module1.a=3
module1.f()

D'autre part, si a est partagé par un grand nombre de modules, mettez-le ailleurs et demandez à tout le monde de l'importer :

import shared_stuff
import module1
shared_stuff.a = 3
module1.f()

et, dans module1.py :

import shared_stuff
def f():
    print shared_stuff.a

Ne fais pas ça. utiliser un from sauf si la variable est destinée à être une constante. from shared_stuff import a créerait un nouveau a initialisée à la variable shared_stuff.a visés au moment de l'importation, et cette nouvelle a ne serait pas affectée par les affectations à la variable shared_stuff.a .


Ou, dans le cas rare où vous avez vraiment besoin qu'il soit vraiment global partout, comme un builtin, ajoutez-le au module builtin. Les détails exacts diffèrent entre Python 2.x et 3.x. En 3.x, cela fonctionne comme suit :

import builtins
import module1
builtins.a = 3
module1.f()

14voto

vishal Points 485

Comme solution de rechange, vous pouvez envisager de définir des variables d'environnement dans la couche externe, comme ceci.

main.py :

import os
os.environ['MYVAL'] = str(myintvariable)

monmodule.py :

import os

myval = None
if 'MYVAL' in os.environ:
    myval = os.environ['MYVAL']

Par précaution supplémentaire, il faut gérer le cas où MYVAL n'est pas défini dans le module.

5voto

kindall Points 60645

Une fonction utilise les globaux du module dont elle est défini dans. Au lieu de mettre a = 3 par exemple, vous devez définir module1.a = 3 . Donc, si vous voulez cur disponible en tant que global dans utilities_module ensemble utilities_module.cur .

Une meilleure solution : ne pas utiliser de globales. Passez les variables dont vous avez besoin dans les fonctions qui en ont besoin, ou créez une classe pour regrouper toutes les données, et passez-la lors de l'initialisation de l'instance.

5voto

user3336548 Points 51

Ce billet n'est qu'une observation du comportement de Python que j'ai rencontré. Peut-être que les conseils que vous lisez ci-dessus ne fonctionnent pas pour vous si vous faites la même chose que moi ci-dessous.

En fait, j'ai un module qui contient des variables globales/partagées (comme suggéré ci-dessus) :

#sharedstuff.py

globaltimes_randomnode=[]
globalist_randomnode=[]

Ensuite, j'avais le module principal qui importe le matériel partagé avec :

import sharedstuff as shared

et quelques autres modules qui ont effectivement peuplé ces tableaux. Ces modules sont appelés par le module principal. En quittant ces autres modules, je peux clairement voir que les tableaux sont remplis. Mais lorsque je les relis dans le module principal, ils sont vides. C'était plutôt étrange pour moi (je suis nouveau en Python). Cependant, lorsque je change la façon dont j'importe sharedstuff.py dans le module principal en :

from globals import *

cela a fonctionné (les tableaux ont été remplis).

Je dis ça comme ça.

3voto

Chris Nasr Points 115

La solution la plus simple à ce problème particulier aurait été d'ajouter une autre fonction dans le module qui aurait stocké le curseur dans une variable globale au module. Toutes les autres fonctions auraient alors pu l'utiliser également.

module1 :

cursor = None

def setCursor(cur):
    global cursor
    cursor = cur

def method(some, args):
    global cursor
    do_stuff(cursor, some, args)

programme principal :

import module1

cursor = get_a_cursor()
module1.setCursor(cursor)
module1.method()

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X