248 votes

Comment faire pour créer des variables à l’échelle du module en Python ?

Est-il possible de définir une variable globale à l'intérieur d'un module? Quand j'ai essayé de le faire de la façon la plus évidente qu'apparaît ci-dessous, l'interpréteur Python, a déclaré la variable __DBNAME__ n'existe pas.

...
__DBNAME__ = None

def initDB(name):
    if not __DBNAME__:
        __DBNAME__ = name
    else:
        raise RuntimeError("Database name has already been set.")
...

Et après l'importation du module dans un fichier différent

...
import mymodule
mymodule.initDB('mydb.sqlite')
...

Et le traceback est: UnboundLocalError: local variable '__DBNAME__' referenced before assignment

Des idées? Je suis en train de configurer un singleton à l'aide d'un module, comme par cet homme de recommandation.

282voto

steveha Points 24808

Voici ce qui se passe.

Tout d'abord, les seules variables globales Python vraiment a sont de module d'étendue variables. Vous ne pouvez pas faire une variable qui est vraiment mondial; tout ce que vous pouvez faire est de faire une variable dans un champ particulier. (Si vous faites une variable à l'intérieur de l'interpréteur Python, et puis les importer d'autres modules, la variable est à l'extérieur de la portée et donc mondiale au sein de votre Python session.)

Tout ce que vous avez à faire pour faire un module variable globale est juste d'attribuer à un nom.

Imaginez un fichier appelé foo.py contenant cette seule ligne:

X = 1

Maintenant, imaginez que vous importez.

import foo
print(foo.X)  # prints 1

Cependant, supposons que vous souhaitez utiliser l'un de vos module-portée des variables global à l'intérieur d'une fonction, comme dans votre exemple. Python par défaut est de supposer que la fonction de variables sont locales. Il suffit d'ajouter un global déclaration dans votre fonction, avant d'essayer d'utiliser le mondial.

def initDB(name):
    global __DBNAME__  # add this line!
    if __DBNAME__ is None: # see notes below; explicit test for None
        __DBNAME__ = name
    else:
        raise RuntimeError("Database name has already been set.")

Par ailleurs, pour cet exemple, le simple if not __DBNAME__ test est suffisant, parce que toute la chaîne de valeur autre qu'une chaîne vide évaluer la valeur true, donc aucun nom de base de données permettra d'évaluer vrai. Mais pour les variables qui peuvent contenir une valeur numérique qui peut être 0, vous ne pouvez pas dire if not variablename; dans ce cas, vous devez explicitement test pour None à l'aide de l' is de l'opérateur. J'ai modifié l'exemple d'ajouter une explicite None de test. Le test explicite pour None ne se trompe jamais, j'ai donc par défaut à utiliser.

Enfin, comme d'autres l'ont noté sur cette page, les deux principaux traits de soulignement signaux de Python que vous voulez de la variable "privé" pour le module. Si jamais vous ne l' import * from mymodule, Python ne pas importer des noms avec les deux principaux traits de soulignement dans votre espace de nom. Mais si vous venez de faire un simple import mymodule puis dire, dir(mymodule) , vous verrez le "privé", les variables dans la liste, et si vous explicitement référence à l' mymodule.__DBNAME__ Python ne sera pas un souci, il sera tout simplement vous permettre de les consulter. Le double principaux traits de soulignement sont un indice majeur pour les utilisateurs de votre module que vous ne voulez pas qu'ils effectuent ce nom à une certaine valeur de leur propre.

Il est considéré comme la meilleure pratique en Python de ne pas faire d' import *, mais pour minimiser le couplage et de maximiser la précision en utilisant mymodule.something ou explicitement d'effectuer une importation comme from mymodule import something.

EDIT: Si, pour une raison quelconque, vous avez besoin de faire quelque chose comme cela dans une très vieille version de Python qui n'ont pas l' global mot-clé, il est facile de solution de contournement. Au lieu de mettre un module variable globale directement, utilisez une mutable type du module niveau mondial, et de stocker vos valeurs à l'intérieur.

Dans vos fonctions, le nom de la variable globale sera en lecture seule, vous ne serez pas en mesure de relier le réel nom de la variable globale. (Si vous attribuez à ce nom de variable à l'intérieur de votre fonction, il affectera seulement le nom de variable locale à l'intérieur de la fonction.) Mais vous pouvez utiliser ce nom de variable locale pour accéder à l'effectif global de l'objet, et de stocker des données à l'intérieur.

Vous pouvez utiliser un list mais votre code sera moche:

__DBNAME__ = [None] # use length-1 list as a mutable

# later, in code:  
if __DBNAME__[0] is None:
    __DBNAME__[0] = name

Un dict , c'est mieux. Mais le plus pratique est une instance de classe, et vous pouvez simplement utiliser un trivial classe:

class Box:
    pass

__m = Box()  # m will contain all module-level values
__m.dbname = None  # database name global in module

# later, in code:
if __m.dbname is None:
    __m.dbname = name

(Vous n'avez pas vraiment besoin pour tirer profit de la base de données nom de la variable).

J'aime le sucre syntaxique de simplement en utilisant __m.dbname plutôt que d' __m["DBNAME"]; il semble que la solution la plus pratique à mon avis. Mais l' dict solution fonctionne très bien aussi.

Avec un dict vous pouvez utiliser n'importe quel hashable valeur comme une clé, mais quand vous êtes heureux avec des noms qui sont des identificateurs valides, vous pouvez utiliser un trivial classe comme Box dans le ci-dessus.

8voto

Chinmay Kanchi Points 16353

Pour ce faire, vous devez déclarer la variable comme mondial. Toutefois, une variable globale est également accessible depuis l' extérieur du module à l’aide de `` . Ajouter ceci comme la première ligne de votre module :

-9voto

wisty Points 4280

Vous êtes en train de tomber pour un subtil clin d'œil. Vous ne pouvez pas ré-assigner les variables de niveau module à l'intérieur d'une fonction python. Je pense que c'est là pour empêcher les gens de re-affectation de trucs à l'intérieur d'une fonction par accident.

Vous pouvez accéder au module de l'espace de noms, vous ne devriez en aucun cas essayer de ré-attribuer. Si votre fonction attribue quelque chose, il devient automatiquement une fonction de la variable et python ne pas regarder dans le module de l'espace de noms.

Vous pouvez le faire:

__DB_NAME__ = None

def func():
    if __DB_NAME__:
        connect(__DB_NAME__)
    else:
        connect(Default_value)

mais vous ne pouvez pas re-assigner __DB_NAME__ à l'intérieur d'une fonction.

Une solution de contournement:

__DB_NAME__ = [None]

def func():
    if __DB_NAME__[0]:
        connect(__DB_NAME__[0])
    else:
        __DB_NAME__[0] = Default_value

Remarque, je ne suis pas re-affectation de __DB_NAME__, je suis juste une modification de son contenu.

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