Question

J'ai récemment rencontré ce cas de UnboundLocalError , ce qui semble étrange:

import pprint

def main():
    if 'pprint' in globals(): print 'pprint is in globals()'
    pprint.pprint('Spam')
    from pprint import pprint
    pprint('Eggs')

if __name__ == '__main__': main()

Qui produit:

pprint is in globals()
Traceback (most recent call last):
  File "weird.py", line 9, in <module>
    if __name__ == '__main__': main()
  File "weird.py", line 5, in main
    pprint.pprint('Spam')
UnboundLocalError: local variable 'pprint' referenced before assignment

pprint est clairement lié à globals et va être lié à des variables locales dans l'instruction suivante. Quelqu'un peut-il expliquer pourquoi il n’est pas content de résoudre pprint dans la liaison dans globals ici?

Modifier: Grâce aux bonnes réponses, je peux clarifier ma question avec la terminologie appropriée:

Au moment de la compilation, l'identificateur pprint est marqué comme étant local dans le cadre. Le modèle d'exécution n'a-t-il pas de distinction dans le cadre auquel l'identificateur local est lié? Peut-il dire, "faire référence à la liaison globale jusqu'à cette instruction de bytecode, à quel point il a été rebondi à une liaison locale," ou le modèle d’exécution ne tient-il pas compte de cela?

Était-ce utile?

La solution

On dirait que Python voit le dans la ligne pprint de pprint import et marque pprint comme un nom local pour main () avant en exécutant n'importe quel code. Puisque Python pense que pprint devrait être une variable locale, il est recommandé de la référencer avec pprint.pprint () avant "assigning". avec l’instruction from..import , cette erreur est générée.

C’est tout ce que je peux en dire.

Naturellement, la morale est de toujours placer ces instructions import en haut de la portée.

Autres conseils

Où est la surprise? Toute variable globale à une étendue que vous réaffectez dans cette étendue est marquée comme locale à cette étendue par le compilateur.

Si les importations étaient traitées différemment, cela serait surprenant à mon humble avis.

Cela peut justifier de ne pas nommer les modules après les symboles qui y sont utilisés, ou inversement.

Eh bien, c’était assez intéressant pour que j’expérimente un peu et je lisais http: / /docs.python.org/reference/executionmodel.html

Ensuite, vous avez bricolé votre code ici et là, voici ce que j'ai pu trouver:

code:

import pprint

def two():
    from pprint import pprint
    print globals()['pprint']
    pprint('Eggs')
    print globals()['pprint']

def main():
    if 'pprint' in globals():
        print 'pprint is in globals()'
    global  pprint
    print globals()['pprint']
    pprint.pprint('Spam')
    from pprint import pprint
    print globals()['pprint']
    pprint('Eggs')

def three():
    print globals()['pprint']
    pprint.pprint('Spam')

if __name__ == '__main__':
    two()
    print('\n')
    three()
    print('\n')
    main()

sortie:

<module 'pprint' from '/usr/lib/python2.5/pprint.pyc'>
'Eggs'
<module 'pprint' from '/usr/lib/python2.5/pprint.pyc'>

<module 'pprint' from '/usr/lib/python2.5/pprint.pyc'>
'Spam'

pprint is in globals()
<module 'pprint' from '/usr/lib/python2.5/pprint.pyc'>
'Spam'
<function pprint at 0xb7d596f4>
'Eggs'

Dans la méthode two () de pprint import pprint , mais ne remplace pas le nom pprint dans globals , puisque le mot clé global n'est pas utilisé dans le cadre de deux () .

Dans la méthode three () puisqu'il n'y a pas de déclaration de pprint nom dans la portée locale, le nom global pprint est défini par défaut. module

Alors que dans main () , le mot clé global est d'abord utilisé de sorte que toutes les références à pprint dans l'étendue de la méthode main () fera référence au nom global , pprint . Comme nous pouvons le voir, il s’agit au départ d’un module qui est remplacé dans l’espace global avec une méthode comme nous le faisons le à partir de pprint import pprint

Bien que cela ne réponde peut-être pas à la question en tant que telle, mais néanmoins, je pense que c'est un fait intéressant.

=======================

Modifier Autre chose intéressante.

Si vous avez un module, dites:

mod1

from datetime import    datetime

def foo():
    print "bar"

et une autre méthode dit:

mod2

import  datetime
from mod1 import *

if __name__ == '__main__':
    print datetime.datetime.now()

ce qui semble à première vue correct puisque vous avez importé le module datetime dans mod2 .

maintenant si vous essayez d'exécuter mod2 en tant que script, une erreur sera générée:

Traceback (most recent call last):
  File "mod2.py", line 5, in <module>
    print datetime.datetime.now()
AttributeError: type object 'datetime.datetime' has no attribute 'datetime'

car la deuxième importation de mod2 import * a remplacé le nom datetime dans l'espace de noms, d'où le premier import datetime n'est plus valide .

Morale: ainsi, l'ordre des importations, la nature des importations (depuis x import *) et la notoriété des importations dans les modules importés - sont importantes .

Cette question a reçu une réponse il y a plusieurs semaines, mais je pense pouvoir clarifier un peu les réponses. D'abord quelques faits.

1: en Python,

import foo

est presque exactement la même chose que

foo = __import__("foo", globals(), locals(), [], -1)

2: lors de l'exécution de code dans une fonction, si Python rencontre une variable qui n'a pas encore été définie dans la fonction, il recherche dans la portée globale.

3: Python utilise une optimisation pour les fonctions appelées "locals". Lorsque Python marque une fonction, il enregistre toutes les variables que vous affectez. Il attribue à chacune de ces variables un nombre provenant d'un entier local croissant de manière monotone. Lorsque Python exécute la fonction, il crée un tableau avec autant d'emplacements qu'il y a de variables locales et attribue à chaque emplacement une valeur spéciale indiquant que "n'a pas encore été affecté", et c'est là que sont stockées les valeurs de ces variables. . Si vous faites référence à un local qui n'a pas encore été affecté, Python voit cette valeur spéciale et lève une exception UnboundLocalValue.

La scène est maintenant définie. Votre " from pprint import pprint " est vraiment une forme de mission. Python crée donc une variable locale appelée "pprint". qui obstrue la variable globale. Ensuite, lorsque vous vous référez à "pprint.pprint" dans la fonction, vous frappez la valeur spéciale et Python lève l'exception. Si vous n'aviez pas cette instruction d'importation dans la fonction, Python utiliserait la résolution de recherche globale dans les globaux d'abord puis dans globaux et rechercherait le module pprint dans les globaux.

Pour éviter toute ambiguïté, vous pouvez utiliser le paramètre "global". mot-clé. Bien entendu, vous avez déjà dépassé votre problème et je ne sais pas si vous aviez réellement besoin de "global". ou si une autre approche était requise.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top