2013-02-16 2 views
7

Я определил три функции, которые должны изменить глобальную переменную x.Разница между «глобальным» и «импортом __main__»

def changeXto1(): 
    global x 
    x = 1 

def changeXto2(): 
    from __main__ import x 
    x = 2 

def changeXto3(): 
    import __main__ 
    __main__.x = 3 

x = 0 
print x 
changeXto1() 
print x 
changeXto2() 
print x 
changeXto3() 
print x 

Это дает результат:

0 
1 
1 
3 

changeXto1 использует обычное глобальное заявление. Результат как ожидалось x == 1. changeXto2 использует from __main__ import по адресу x. Это не работает. Впоследствии x по-прежнему 1. changeXto3 использует import main по адресу x через __main__.x. В результате результат равен 3, как ожидалось.

Почему не работает в changeXto2, а import __main__ работает в changeXto3? Зачем нам нужен глобальный оператор в Python, если мы можем обращаться к глобальным переменным также с модулем __main__?

+0

changeXto2 только устанавливает локальную переменную: http://www.saltycrane.com/blog/2008/01/python-variable- scope-notes/ –

+0

Я ничего не вижу об импорте модулей. –

ответ

10

Это связано с тем, как Python преобразует ваш код в байт-код (шаг компиляции).

При компиляции функции Python обрабатывает всю переменную, назначаемую как локальную переменную, и выполняет оптимизацию, чтобы уменьшить количество поисков имен, которые она должна будет выполнять. Каждой локальной переменной присваивается индекс, а когда функция вызывается, их значение будет храниться в локальном массиве стека, адресованном индексом. Компилятор выдаст код LOAD_FAST и STORE_FAST для доступа к переменной.

Синтаксис global указывает вместо этого компилятору, что даже если переменной присваивается значение, ее нельзя рассматривать как локальную переменную, не следует назначать индекс. Вместо этого он будет использовать код LOAD_GLOBAL и STORE_GLOBAL для доступа к переменной. Эти коды операций медленнее, так как они используют имя для поиска в возможно многих словарях (locals, globals).

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

Итак, в вашей первой функции, используя global x, компилятор сообщает компилятору, что он должен обрабатывать доступ на запись к x как запись глобальной переменной вместо локальной переменной. Коды операций для функции дают понять:

>>> dis.dis(changeXto1) 
    3   0 LOAD_CONST    1 (1) 
       3 STORE_GLOBAL    0 (x) 
       6 LOAD_CONST    0 (None) 
       9 RETURN_VALUE   

В вашем третьем примере, вы импортировать __main__ модуль в локальную переменную с именем __main__, а затем присвоить его x поле. Поскольку модуль является объектом, который хранит все отображения верхнего уровня в виде полей, вы назначаете переменную x в модуле __main__. И, как вы нашли, поля модуля __main__ непосредственно отображают значения в словаре globals(), потому что ваш код определен в модуле __main__. В опкоды показывают, что у вас нет доступа x непосредственно:

>>> dis.dis(changeXto3) 
    2   0 LOAD_CONST    1 (-1) 
       3 LOAD_CONST    0 (None) 
       6 IMPORT_NAME    0 (__main__) 
       9 STORE_FAST    0 (__main__) 

    3   12 LOAD_CONST    2 (3) 
      15 LOAD_FAST    0 (__main__) 
      18 STORE_ATTR    1 (x) 
      21 LOAD_CONST    0 (None) 
      24 RETURN_VALUE   

Второй пример интересен. Поскольку вы назначаете значение переменной x, компилятор предполагает, что это локальная переменная и выполняет оптимизацию. Затем from __main__ import x импортирует модуль __main__ и создает новое связывание значения x в модуле __main__ с локальной переменной с именем x. Это всегда так, from ${module} import ${name} просто создайте новое связывание текущего пространства имен. Когда вы назначаете новое значение переменной x, вы просто меняете текущую привязку, а не привязку в модуле __main__, которая не связана (хотя, если значение изменено и вы его мутируете, изменение будет видно через все привязки). Вот опкоды:

>>> dis.dis(f2) 
    2   0 LOAD_CONST    1 (-1) 
       3 LOAD_CONST    2 (('x',)) 
       6 IMPORT_NAME    0 (__main__) 
       9 IMPORT_FROM    1 (x) 
      12 STORE_FAST    0 (x) 
      15 POP_TOP    

    3   16 LOAD_CONST    3 (2) 
      19 STORE_FAST    0 (x) 
      22 LOAD_CONST    0 (None) 
      25 RETURN_VALUE   

Хороший способ думать об этом является то, что в Python все присваивания являются обязательными имя на значение в словаре, и разыменования просто делает поиск по словарю (это грубое приближение , но довольно близко к концептуальной модели). Когда вы делаете obj.field, то вы ищете скрытый словарь obj (доступно через obj.__dict__) для ключа "field".

Если у вас есть голое имя переменной, оно отображается в словаре locals(), а затем в словаре globals(), если оно отличается (они одинаковы, когда код выполняется на уровне модуля). Для присваивания он всегда помещает привязку в словарь locals(), если вы не заявили, что хотите получить глобальный доступ, выполнив global ${name} (этот синтаксис также работает на верхнем уровне).

Итак перевод вашу функцию, это почти, если бы вы написали:

# NOTE: this is valid Python code, but is less optimal than 
# the original code. It is here only for demonstration. 

def changeXto1(): 
    globals()['x'] = 1 

def changeXto2(): 
    locals()['x'] = __import__('__main__').__dict__['x'] 
    locals()['x'] = 2 

def changeXto3(): 
    locals()['__main__'] = __import__('__main__') 
    locals()['__main__'].__dict__['x'] = 3 
+0

Благодарим вас за этот исчерпывающий ответ. Я всегда думал, что 'import module' и' from import import 'совпадают, за исключением того, что переменные вызываются по-разному. Я вижу, что есть разница, когда новая переменная создается с помощью 'from import'. Переменная в исходном модуле поддерживается одинаково, но переменная в локальном пространстве имен создается и привязана к локальному имени. – Holger

8

Почему не from __main__ import работа в changeXto2, в то время как import __main__ работает в changeXto3?

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

Зачем нам глобальный оператор в Python, если мы можем обращаться к глобальным переменным также с модулем __main__?

Потому что они только делают то же самое , когда ваш код работает в __main__. Если вы используете, скажем, othermodule после импорта, то __main__ будет ссылаться на основной сценарий и неothermodule.

Смежные вопросы