web-dev-qa-db-fra.com

Visibilité des variables globales dans les modules importés

J'ai rencontré quelques difficultés à importer 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 d'utilitaires, qui font référence à des entités définies dans l'espace de noms dans lequel ce module auxiliaire sera importé (laissez "a" être 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 que cela corresponde à 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. Pour des raisons de propreté, j'ai défini le groupe 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 à l'intérieur le module utilitaires, et qui est l'objet curseur du module MySQLdb . J'ai réalisé par la suite que le curseur objet (utilisé pour communiquer avec le serveur de base de données) doit être défini dans le module principal, , de sorte que le module principal et tout ce qui y est importé puissent accéder à cet objet.

Le résultat final ressemblerait à 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'essaie d'appeler l'une des fonctions utilitaires, l'erreur "nom global non défini" susmentionnée est déclenchée.

Une suggestion particulière était d'avoir une instruction "from program import cur" dans le fichier utilities, 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 du genre et, en fin de compte, elle se bloque aussi. Donc ma question est:

Comment puis-je rendre l'objet "cur", défini dans le module principal, visible pour 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.

70
Nubarke

Les globales en Python sont globales pour un module , pas pour tous les modules. (Beaucoup de gens sont déconcertés par cela, parce que dans C, disons, un global est identique dans tous les fichiers d'implémentation, à moins que vous ne le rendiez 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 suivre cette voie, demandez-vous si cela doit vraiment être mondial. Peut-être que vous voulez vraiment une classe, avec f comme méthode d'instance, plutôt que juste une fonction libre? Ensuite, vous pourriez faire quelque chose comme ça:

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

Si vous voulez vraiment un global, mais qu'il soit juste là pour être utilisé par module1, définissez-le dans ce module.

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

D'un autre côté, si a est partagé par un grand nombre de modules, mettez-le ailleurs et faites-le importer par tout le monde:

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 pas utiliser une importation from à moins que la variable ne soit supposée être une constante. from shared_stuff import a créerait une nouvelle variable a initialisée avec le shared_stuff.a auquel il est fait référence au moment de l'importation, et cette nouvelle variable a ne serait pas affectée par les assignations à shared_stuff.a.


Ou, dans les rares cas où vous en avez vraiment besoin pour être véritablement mondial, 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:

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

Pour résoudre ce problème, vous pouvez définir des variables d’environnement dans la couche externe, comme ceci. 

main.py:

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

mymodule.py:

import os
print os.environ['MYVAL']

Par précaution supplémentaire, gérez le cas où MYVAL n'est pas défini à l'intérieur du module. 

5
Vishal

Une fonction utilise les globales du module, il est défini in. Au lieu de définir a = 3, par exemple, vous devriez définir module1.a = 3. Donc, si vous voulez que cur soit disponible globalement dans utilities_module, définissez utilities_module.cur.

Une meilleure solution: n'utilisez pas de globaux. Transmettez 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 transmettez-la lors de l'initialisation de l'instance.

3
kindall

Ce message est juste une observation pour le comportement de Python que j'ai rencontré. Peut-être que les conseils que vous avez lus ci-dessus ne vous conviennent pas si vous avez fait la même chose que ci-dessous.

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

#sharedstuff.py

globaltimes_randomnode=[]
globalist_randomnode=[]

Ensuite, j'ai eu le module principal qui importe le contenu partagé avec:

import sharedstuff as shared

et quelques autres modules qui ont réellement peuplé ces tableaux. Celles-ci sont appelées 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 en Python). Cependant, lorsque je modifie la façon dont j'importe le sharedstuff.py du module principal pour:

from globals import *

cela a fonctionné (les tableaux ont été peuplés).

Juste dire

3
user3336548

La solution la plus simple à ce problème aurait été d’ajouter une autre fonction dans le module qui aurait stocké le curseur dans une variable globale du module. Ensuite, toutes les autres fonctions pourraient également l’utiliser.

module 1:

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()
2
Chris Nasr

Comme les globales sont spécifiques à un module, vous pouvez ajouter la fonction suivante à tous les modules importés, puis l’utiliser pour:

  • Ajouter des variables singulières (au format dictionnaire) en tant que globales pour celles-ci 
  • Transférez-y vos globals main module .

addglobals = lambda x: globals (). update (x)

Ensuite, tout ce dont vous avez besoin pour transmettre les globals actuels est: 

module d'importation

module.addglobals (globals ())

1
user2589273

Pour ce faire, OOP consisterait à faire de votre module une classe au lieu d'un ensemble de méthodes non liées. Ensuite, vous pouvez utiliser __init__ ou une méthode de définition pour définir les variables de l'appelant à utiliser dans les méthodes de module.

0
coyot

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 argument global_dict à la fonction nécessitant les globales du module appelant, puis à passer le dict dans la fonction lors de l'appel. par exemple:

# external_module
def imported_function(global_dict=None):
    print(global_dict["a"])


# calling_module
a = 12
from external_module import imported_function
imported_function(global_dict=globals())

>>> 12
0
Toby Petty