2013-03-13 2 views
3

J'essaie d'étaler mon code sur des fichiers séparés pour améliorer la lisibilité, et je rencontre des problèmes avec des noms globaux indéfinis référencés dans des fichiers importés. Y at-il une meilleure solution que de passer délibérément toutes les valeurs nécessaires aux fonctions lors de l'appel?Variables globales dans les fonctions de classe importées en Python

Mon code est actuellement comme ceci:

#foo.py 

import bar 

def main(): 
    a = 1 
    b = bar.Bar() 
    while True: 
     a += 1 
     b.incr() 
     print a 
     print b.c 

if __name__ == '__main__': 
    main() 

#END foo.py 

-

#bar.py 

class Bar: 
    def __init__(self): 
     self.c = 0 
    def incr(self): 
     self.c += a 

#END bar.py 

Il me donne NameError: nom global 'a' est pas défini. Ai-je besoin de réécrire main() comme ceci:

def main(): 
     b = new bar.Bar() 
     while True: 
      a += 1 
      b.incr(a) 
      print a 
      print b.c 

Et réécrire INCR() comme ceci:

def incr(self,a): 
      c += a 

Ou est-il une meilleure façon?

Pour mémoire, le code ci-dessus est fortement abstrait. Le code actuel implique un grand nombre de classes et de fonctions passant plusieurs variables de plusieurs types, y compris certains grands dictionnaires.

Merci d'avance!

+2

Puisque les variables globales ne sont généralement pas une bonne idée, passer des variables de fonction à fonction est la meilleure option. –

+0

en passant un à 'incr()' est une meilleure idée – PurityLake

+0

Merci @PurityLake et MartijnPieters - en ce qui concerne la performance, est-ce pas un problème? Existe-t-il une bonne solution pour passer de grandes quantités de variables de la fonction à la fonction? Collez-les tous dans un dictionnaire, ou un dictionnaire de dictionnaires, peut-être? – yoel

Répondre

3

Si vous voulez juste savoir pourquoi il ne fonctionne pas:

D'abord, main n'est pas une mise en a globale, mais un local. Si vous voulez faire global, vous devez être explicite:

def main(): 
    global a 
    a = 1 
    # ... 

Mais cela ne résoudra pas le problème, parce que « global » en Python est par module. En d'autres termes, cela crée simplement un foo.a, mais votre code dans bar.Bar cherchera bar.a, qui n'existera pas. Vous pouvez import foo puis accéder au foo.a si vous le souhaitez. (Dans ce cas, la dépendance circulaire ne devrait pas être un problème, et le if __name__ == '__main__' ne sera pas exécuté deux fois.) Ou vous pouvez supposer que foo a été importé et utiliser sys.modules['foo'].a. Ou vous pouvez injecter a dans le dictionnaire bar de foo. Ou beaucoup d'autres astuces. Mais ce sont toutes des choses horribles à faire.

Si vous ne pouvez pas faire un travail global, la bonne réponse est presque toujours de ne pas utiliser un global. (En fait, même lorsque vous pouvez faire un travail global, c'est généralement la bonne réponse.)

Alors, comment faites-vous cela? Sans en savoir plus sur les détails, il est difficile de deviner si a appartient en tant que module explicite global, un attribut de classe, un attribut d'instance, un paramètre à incr, etc. Mais déterminez lequel est le plus logique compte tenu de ce que représente a, et fais ça.


Dans les commentaires, vous avez suggéré que vous ayez un tas de ces variables de configuration. Si vous les enveloppez tous dans un dict et passez ce dict au constructeur de chaque objet, cela simplifiera les choses. Plus important encore, un dict est une référence à un objet mutable. La copier fait juste une autre référence à ce même objet.

main.py:

def main(): 
    configs = {} 
    configs['gravity'] = 1.0 
    rock = rps.Rock(configs) 
    rock.do_stuff() 
    configs['gravity'] = 2.1 
    rock.do_stuff() 
if __name__ == '__main__': 
    main() 

rps.py:

class Rock(object): 
    def __init__(self, configs): 
     self.configs = configs 
    def do_stuff(self): 
     print('Falling with gravity {}.'.format(self.configs['gravity'])) 

Lorsque vous exécutez cela, il imprimerons quelque chose comme:

Falling with gravity 1.0. 
Falling with gravity 2.1. 

Vous n'avez pas modifié la valeur configs['gravity'] (c'est un flottant immuable, vous ne pouvez pas le modifier), mais vous avez l'a remplacé par une valeur différente (car configs est un mutable dict; vous pouvez le modifier très bien). Vous pouvez essayer d'imprimer des choses comme id(self.configs), id(self.configs['gravity']), etc. à divers endroits si l'identité n'est pas claire.

+0

Merci - juste pour m'assurer de bien comprendre: si 'configs ['gravity']' est égal à 1 et je dis 'configs 'gravité '] = 2', ça supprime l'ancienne' 'gravité' 'et en fait une nouvelle? Donc, quand un objet créé précédemment va le chercher, il ne trouve que la nouvelle valeur? – yoel

+0

@yoel: Oui, vous l'avez. Quand vous faites 'configs ['gravity'] = 2', toute personne qui fait' configs ['gravity'] 'après ça - ou' foo ['gravity'] 'si' foo' est une référence au même dictionnaire que ' configs'-verra '2'. – abarnert

1

Si a est une caractéristique ou une configuration de Bar, il devrait s'agir d'un attribut d'instance ou de classe de celui-ci.

class Bar(object): 
    class_step = 1 
    def __init__(self, inst_step): 
     self.inst_step = inst_step 
     self.c = 0 
     self.step_mode = 'class' 
    def inc(self): 
     self.c += Bar.class_step if self.step_mode == 'class' else self.inst_step 
+0

Merci - ce n'est pas. Disons que 'a' est utilisé non seulement par' Bar', mais aussi par 'Spam',' Eggs' et 'Toast'. – yoel

+2

Puis partager un objet de configuration entre toutes les classes qui ont besoin d'utiliser les données ... –

Questions connexes