Глобальные файлы Python, локальные файлы и UnboundLocalError

StackOverflow https://stackoverflow.com/questions/404534

Вопрос

Я наткнулся на этот случай UnboundLocalError недавно, что кажется странным:

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()

Который производит:

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 четко связан с globals, и будет связан в locals в следующем заявлении.Может ли кто-нибудь предложить объяснение, почему это не приносит счастья? pprint к привязке в globals здесь?

Редактировать: Благодаря хорошим ответам я могу прояснить свой вопрос с помощью соответствующей терминологии:

Во время компиляции идентификатор pprint помечен как локальный для фрейма.Имеет ли модель выполнения какие-либо различия где внутри фрейма привязан локальный идентификатор?Может ли он сказать: "обратитесь к глобальной привязке вплоть до этой инструкции байт-кода, после чего она будет восстановлена до локальной привязки", или модель выполнения этого не учитывает?

Это было полезно?

Решение

Похоже, Python видит from pprint import pprint линии и метки pprint как имя, локальное для main() до того, как выполнение любого кода.Поскольку Python считает, что pprint должна быть локальной переменной, ссылающейся на нее с помощью pprint.pprint() прежде чем "назначить" его с помощью from..import оператор, он выдает эту ошибку.

Это все, что я могу из этого извлечь.

Мораль, конечно, состоит в том, чтобы всегда ставить эти import утверждения в верхней части области видимости.

Другие советы

Где же сюрприз? Любой переменная, глобальная для области, которую вы переназначаете в этой области, помечается компилятором как локальная для этой области.

Если бы импорт обрабатывался по-другому, это было бы удивительно, имхо.

Однако это может привести к тому, что модули не будут именоваться в честь используемых в них символов, или наоборот.

Что ж, это было достаточно интересно для меня, чтобы немного поэкспериментировать, и я дочитал до конца http://docs.python.org/reference/executionmodel.html

Затем немного повозился с вашим кодом здесь и там, вот что я смог найти:

код:

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()

выходной сигнал:

<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'

В методе two() from pprint import pprint но не переопределяет имя pprint в globals, с тех пор как global ключевое слово - это нет используется в рамках two().

В методе three() поскольку нет объявления о pprint имя в локальной области по умолчанию используется глобальное имя pprint который является модулем

Принимая во внимание , что в main(), сначала ключевое слово global используется таким образом, все ссылки на pprint в рамках метода main() будем ссылаться на global Имя pprint.Который, как мы можем видеть, сначала является модулем и переопределяется в global namespace с помощью метода, по мере того как мы делаем from pprint import pprint

Хотя это, возможно, и не отвечает на вопрос как таковой, но, тем не менее, я думаю, что это какой-то интересный факт.

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

Редактировать Еще одна интересная вещь.

Если у вас есть модуль, скажите:

mod1

from datetime import    datetime

def foo():
    print "bar"

и другой метод, скажем:

mod2

import  datetime
from mod1 import *

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

что на первый взгляд кажется правильным, поскольку вы импортировали модуль datetime в mod2.

теперь, если вы попытаетесь запустить mod2 как скрипт, он выдаст ошибку:

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'

потому что второй импорт from mod2 import * переопределил имя datetime в пространстве имен, следовательно, первый import datetime больше не действителен.

Моральный:Таким образом, порядок импорта, характер импорта (из x import *) и осведомленность об импорте в импортируемых модулях - имеет значение.

На этот вопрос был дан ответ несколько недель назад, но я думаю, что могу немного прояснить ответы.Сначала несколько фактов.

1:В Python,

import foo

это почти точно то же самое, что и

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

2:При выполнении кода в функции, если Python обнаруживает переменную, которая еще не была определена в функции, она просматривается в глобальной области видимости.

3:В Python есть оптимизация, которую он использует для функций, называемых "локальными".Когда Python маркирует функцию, он отслеживает все переменные, которым вы присваиваете значение.Он присваивает каждой из этих переменных число из локального монотонно возрастающего целого числа.Когда Python запускает функцию, она создает массив с таким количеством слотов, сколько существует локальных переменных, и присваивает каждому слоту специальное значение, которое означает "еще не присвоено", и именно там хранятся значения для этих переменных.Если вы ссылаетесь на local, которому еще не было присвоено значение, Python видит это специальное значение и выдает исключение UnboundLocalValue.

Теперь сцена готова.Ваше "from pprint импортировать pprint" на самом деле является формой присвоения.Итак, Python создает локальную переменную с именем "pprint", которая перекрывает глобальную переменную.Затем, когда вы ссылаетесь на "pprint.pprint" в функции, вы нажимаете специальное значение, и Python выдает исключение.Если бы у вас не было этого оператора import в функции, Python использовал бы обычное разрешение look-in-locals-first-then-look-in-globals и нашел бы модуль pprint в globals.

Чтобы устранить эту неоднозначность, вы можете использовать ключевое слово "global".Конечно, к настоящему времени вы уже справились со своей проблемой, и я не знаю, действительно ли вам нужен был "глобальный" подход или требовался какой-то другой.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top