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.

3voto

user2589273 Points 1182

Puisque les globaux sont spécifiques aux modules, vous pouvez ajouter la fonction suivante à tous les modules importés, puis l'utiliser pour :

  • Ajouter des variables singulières (au format dictionnaire) comme globales pour ceux
  • Transférez votre principal les globaux du module .

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

Alors tout ce dont vous avez besoin pour transmettre les globaux actuels est :

module d'importation

module.addglobals(globals())

1voto

Toby Petty Points 1792

Comme je ne l'ai pas vu dans les réponses ci-dessus, j'ai pensé que je pourrais ajouter ma solution de contournement simple, qui consiste simplement à ajouter un fichier global_dict à la fonction nécessitant les globaux du module appelant, et ensuite 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

0voto

coyot Points 368

La façon de faire de la POO serait de faire de votre module une classe au lieu d'un ensemble de méthodes non liées. Vous pourriez alors utiliser __init__ ou une méthode setter pour définir les variables de l'appelant afin de les utiliser dans les méthodes du module.

0voto

Orwellophile Points 2695

Mise à jour

Pour tester la théorie, j'ai créé un module et l'ai mis sur pypi. Tout a fonctionné parfaitement.

pip install superglobals

Réponse courte

Cela fonctionne bien dans Python 2 ou 3 :

import inspect

def superglobals():
    _globals = dict(inspect.getmembers(
                inspect.stack()[len(inspect.stack()) - 1][0]))["f_globals"]
    return _globals

sauvegarder en tant que superglobals.py et l'employer dans un autre module ainsi :

from superglobals import *

superglobals()['var'] = value

Réponse étendue

Vous pouvez ajouter quelques fonctions supplémentaires pour rendre les choses plus attrayantes.

def superglobals():
    _globals = dict(inspect.getmembers(
                inspect.stack()[len(inspect.stack()) - 1][0]))["f_globals"]
    return _globals

def getglobal(key, default=None):
    """
    getglobal(key[, default]) -> value

    Return the value for key if key is in the global dictionary, else default.
    """
    _globals = dict(inspect.getmembers(
                inspect.stack()[len(inspect.stack()) - 1][0]))["f_globals"]
    return _globals.get(key, default)

def setglobal(key, value):
    _globals = superglobals()
    _globals[key] = value

def defaultglobal(key, value):
    """
    defaultglobal(key, value)

    Set the value of global variable `key` if it is not otherwise st
    """
    _globals = superglobals()
    if key not in _globals:
        _globals[key] = value

Alors utilisez-le comme ça :

from superglobals import *

setglobal('test', 123)
defaultglobal('test', 456)
assert(getglobal('test') == 123)

Justification

Les réponses à la question "python purity league" sont tout à fait correctes, mais dans certains environnements (comme IDAPython) qui sont essentiellement monofilaires avec une grande API instanciée globalement, il suffit de n'a pas autant d'importance .

Ce n'est toujours pas correct et c'est une mauvaise pratique à encourager, mais parfois c'est simplement plus facile. Surtout lorsque le code que vous écrivez n'a pas une très longue durée de vie.

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