web-dev-qa-db-fra.com

Comment faire une variable inter-module?

La variable __debug__ est utile en partie car elle affecte tous les modules. Si je veux créer une autre variable qui fonctionne de la même manière, comment le ferais-je?

La variable (appelons-la «original») ne doit pas nécessairement être globale, en ce sens que si je change de foo dans un module, elle est mise à jour dans d'autres. Je serais bien si je pouvais mettre foo avant d'importer d'autres modules et ils verraient alors la même valeur.

113
Dan Homerick

Je n'approuve en aucune façon cette solution. Mais si vous ajoutez une variable au module __builtin__, elle sera accessible comme s'il s'agissait d'une variable globale à partir de tout autre module incluant __builtin__ - ce qui correspond à toutes, par défaut.

a.py contient

print foo

b.py contient

import __builtin__
__builtin__.foo = 1
import a

Le résultat est que "1" est imprimé.

Édition: Le module __builtin__ est disponible en tant que symbole local __builtins__ - c’est la raison de l’écart entre deux de ces réponses. Notez également que __builtin__ a été renommé en builtins en python3.

101
Curt Hagenlocher

Si vous avez besoin d'une variable globale inter-modules, il suffira peut-être d'une simple variable globale au niveau du module. 

a.py:

var = 1

b.py:

import a
print a.var
import c
print a.var

c.py:

import a
a.var = 2

Tester:

$ python b.py
# -> 1 2

Exemple concret: global_settings.py de Django -/(bien que dans Django apps, les paramètres sont utilisés en important objectDjango.conf.settings ).

146
jfs

Je pense qu'il y a beaucoup de circonstances dans lesquelles il est logique et simplifie la programmation de disposer de globals connus dans plusieurs modules (étroitement couplés). Dans cet esprit, je voudrais développer un peu l'idée d'un module de globals importé par les modules qui doivent les référencer.

Quand il n'y a qu'un seul module de ce type, je le nomme "g". J'y attribue des valeurs par défaut pour chaque variable que je souhaite traiter comme globale. Dans tous les modules qui en utilisent, je n'utilise pas "from g import var", car il en résulte une variable locale initialisée à partir de g uniquement au moment de l'importation. Je fais plus de références sous la forme g.var, et le "g." me rappelle constamment que j’ai affaire à une variable potentiellement accessible à d’autres modules. 

Si la valeur d'une telle variable globale doit être utilisée fréquemment dans une fonction d'un module, cette fonction peut alors créer une copie locale: var = g.var. Cependant, il est important de réaliser que les assignations à var sont locales et que g.var global ne peut pas être mis à jour sans référencer explicitement g.var dans une assignation.

Notez que vous pouvez également avoir plusieurs tels modules globaux partagés par différents sous-ensembles de vos modules pour garder les choses un peu plus étroitement contrôlées. J'utilise des noms abrégés pour mes modules globals afin d'éviter de surcharger le code avec des occurrences. Avec un peu d'expérience, ils deviennent assez mnémoniques avec seulement 1 ou 2 caractères. 

Il est toujours possible d’affecter g.x, par exemple, lorsque x n’a pas déjà été défini dans g, et un module différent peut alors accéder à g.x. Cependant, même si l'interprète le permet, cette approche n'est pas aussi transparente et je l'évite. Il est toujours possible de créer accidentellement une nouvelle variable dans g à la suite d’une faute de frappe dans le nom de la variable pour une affectation. Parfois, un examen de dir (g) est utile pour découvrir des noms inattendus pouvant être créés par un tel accident.

22
David Vanderschel

Définissez un module (appelez-le "globalbaz") et indiquez les variables qui y sont définies. Tous les modules utilisant ce "pseudoglobal" doivent importer le module "globalbaz" et y faire référence en utilisant "globalbaz.var_name"

Cela fonctionne quel que soit le lieu du changement, vous pouvez modifier la variable avant ou après l'importation. Le module importé utilisera la dernière valeur. (J'ai testé cela dans un exemple de jouet)

Pour plus de clarté, globalbaz.py ressemble à ceci:

var_name = "my_useful_string"
22
hayalci

Vous pouvez passer les globales d'un module à un autre:

Dans le module A:

import module_b
my_var=2
module_b.do_something_with_my_globals(globals())
print my_var

Dans le module B:

def do_something_with_my_globals(glob): # glob is simply a dict.
    glob["my_var"]=3
9
user394430

Les variables globales sont généralement une mauvaise idée, mais vous pouvez le faire en attribuant à __builtins__:

__builtins__.foo = 'something'
print foo

En outre, les modules eux-mêmes sont des variables auxquelles vous pouvez accéder depuis n'importe quel module. Donc, si vous définissez un module appelé my_globals.py:

# my_globals.py
foo = 'something'

Ensuite, vous pouvez l'utiliser de n'importe où:

import my_globals
print my_globals.foo

Utiliser des modules plutôt que de modifier __builtins__ est généralement une manière plus simple de faire des globals de ce type.

7
spiv

Vous pouvez déjà le faire avec des variables de niveau module. Les modules sont les mêmes quel que soit le module depuis lequel ils sont importés. Ainsi, vous pouvez faire de la variable une variable de niveau module dans le module de votre choix, puis y accéder ou l’affecter à partir d’autres modules. Il serait préférable d'appeler une fonction pour définir la valeur de la variable ou d'en faire une propriété d'un objet singleton. Ainsi, si vous devez exécuter du code lorsque la variable est modifiée, vous pouvez le faire sans endommager l'interface externe de votre module.

Ce n’est généralement pas une bonne façon de faire les choses - l’utilisation des globaux le est rarement - mais je pense que c’est la façon la plus propre de le faire. 

5
intuited

Je voulais poster une réponse qu'il existe un cas où la variable ne sera pas trouvée.

Les importations cycliques peuvent casser le comportement du module.

Par exemple:

premier.py

import second
var = 1

seconde.py

import first
print(first.var)  # will throw an error because the order of execution happens before var gets declared.

main.py

import first

Sur cet exemple, cela devrait être évident, mais dans une grande base de code, cela peut être très déroutant.

3
Jonathan

Cela ressemble à une modification de l’espace de noms __builtin__. Pour le faire:

import __builtin__
__builtin__.foo = 'some-value'

N'utilisez pas le __builtins__ directement (remarquez le "s" supplémentaire) - cela peut apparemment être un dictionnaire ou un module. Merci à ΤΖΩΤΖΙΟΥ de nous l'avoir signalé, vous pouvez en trouver davantage ici .

Maintenant, foo est disponible pour une utilisation partout.

Je ne recommande pas de faire ceci en général, mais l'utilisation de ceci est laissée au programmeur.

L'affectation à cela doit être effectuée comme ci-dessus, le simple fait de définir foo = 'some-other-value' ne le définira que dans l'espace de noms actuel.

2
awatts

J'utilise ceci pour un couple de fonctions primitives intégrées qui me manquaient vraiment. Un exemple est une fonction de recherche qui a la même sémantique d'utilisation que filtre, mapper, réduire.

def builtin_find(f, x, d=None):
    for i in x:
        if f(i):
            return i
    return d

import __builtin__
__builtin__.find = builtin_find

Une fois que cela est exécuté (par exemple, en important près de votre point d’entrée), tous vos modules peuvent utiliser find () comme si, évidemment, il était intégré.

find(lambda i: i < 0, [1, 3, 0, -5, -10])  # Yields -5, the first negative.

Remarque: Vous pouvez le faire, bien sûr, avec un filtre et une autre ligne pour tester une longueur nulle, ou avec une réduction dans une sorte de ligne étrange, mais j'ai toujours pensé que c'était bizarre.

1
Brian Arsuaga

Je me demandais s’il serait possible d’éviter certains des inconvénients de l’utilisation de variables globales (voir par exemple http://wiki.c2.com/?GlobalVariablesAreBad ) en utilisant un espace de noms de classe plutôt transmettre les valeurs des variables. Le code suivant indique que les deux méthodes sont essentiellement identiques. L'utilisation des espaces de noms de classe présente un léger avantage, comme expliqué ci-dessous. 

Les fragments de code suivants montrent également que des attributs ou des variables peuvent être créés et supprimés de manière dynamique dans les espaces de nom global/module et les espaces de nom de classe. 

wall.py

# Note no definition of global variables

class router:
    """ Empty class """

J'appelle ce module 'wall' puisqu'il est utilisé pour faire rebondir des variables. Il servira d'espace pour définir temporairement les variables globales et les attributs de la classe vide "routeur".

source.py

import wall
def sourcefn():
    msg = 'Hello world!'
    wall.msg = msg
    wall.router.msg = msg

Ce module importe un mur et définit une seule fonction sourcefn qui définit un message et l’émet par deux mécanismes différents, l’un via les fonctions globales et l’autre via la fonction de routeur. Notez que les variables wall.msg et wall.router.message sont définies ici pour la première fois dans leurs espaces de noms respectifs.

dest.py

import wall
def destfn():

    if hasattr(wall, 'msg'):
        print 'global: ' + wall.msg
        del wall.msg
    else:
        print 'global: ' + 'no message'

    if hasattr(wall.router, 'msg'):
        print 'router: ' + wall.router.msg
        del wall.router.msg
    else:
        print 'router: ' + 'no message'

Ce module définit une fonction destfn qui utilise les deux mécanismes différents pour recevoir les messages émis par la source. Cela laisse à penser que la variable 'msg' peut ne pas exister. destfn supprime également les variables une fois qu'elles ont été affichées.

main.py

import source, dest

source.sourcefn()

dest.destfn() # variables deleted after this call
dest.destfn()

Ce module appelle les fonctions précédemment définies en séquence. Après le premier appel à dest.destfn, les variables wall.msg et wall.router.msg n'existent plus.

Le résultat du programme est:

global: Bonjour tout le monde!
routeur: Bonjour tout le monde!
global: pas de message
routeur: pas de message 

Les fragments de code ci-dessus montrent que les mécanismes de module/global et de classe/classe sont essentiellement identiques. 

Si de nombreuses variables doivent être partagées, la pollution des espaces de noms peut être gérée à l'aide de plusieurs modules de type mur, par exemple. wall1, wall2, etc. ou en définissant plusieurs classes de type routeur dans un même fichier. Ce dernier est légèrement plus ordonné, ce qui représente peut-être un avantage marginal pour l’utilisation du mécanisme de variable de classe. 

0
robertofbaycot

Je pouvais obtenir des variables modifiables (ou mutables ) inter-modules en utilisant un dictionnaire:

# in myapp.__init__
Timeouts = {} # cross-modules global mutable variables for testing purpose
Timeouts['WAIT_APP_UP_IN_SECONDS'] = 60

# in myapp.mod1
from myapp import Timeouts

def wait_app_up(project_name, port):
    # wait for app until Timeouts['WAIT_APP_UP_IN_SECONDS']
    # ...

# in myapp.test.test_mod1
from myapp import Timeouts

def test_wait_app_up_fail(self):
    timeout_bak = Timeouts['WAIT_APP_UP_IN_SECONDS']
    Timeouts['WAIT_APP_UP_IN_SECONDS'] = 3
    with self.assertRaises(hlp.TimeoutException) as cm:
        wait_app_up(PROJECT_NAME, PROJECT_PORT)
    self.assertEqual("Timeout while waiting for App to start", str(cm.exception))
    Timeouts['WAIT_JENKINS_UP_TIMEOUT_IN_SECONDS'] = timeout_bak

Lors du lancement de test_wait_app_up_fail, la durée de temporisation réelle est de 3 secondes.

0
foudfou