Подтвердить что ты не робот

Глобальные словари не нуждаются в ключевом слове global для их изменения?

Возможный дубликат:
Почему в этом случае не требуется ключевое слово global?

Интересно, почему я могу изменить глобальный словарь без ключевого слова global? Почему он является обязательным для других типов? Есть ли какая-то логика?

например. код:

#!/usr/bin/env python3

stringvar = "mod"
dictvar = {'key1': 1,
           'key2': 2}

def foo():
    dictvar['key1'] += 1

def bar():
    stringvar = "bar"
    print(stringvar)

print(dictvar)
foo()
print(dictvar)

print(stringvar)
bar()
print(stringvar)

Дает следующие результаты:

[email protected]:~/$ ./globalDict.py 
{'key2': 2, 'key1': 1}
{'key2': 2, 'key1': 2}  # Dictionary value has been changed
mod
bar
mod

где я ожидал бы:

[email protected]:~/$ ./globalDict.py 
{'key2': 2, 'key1': 1}
{'key2': 2, 'key1': 1}  # I didn't use global, so dictionary remains the same
mod
bar
mod
4b9b3361

Ответ 1

Причина в том, что строка

stringvar = "bar"

является неоднозначным, он может ссылаться на глобальную переменную или создавать новую локальную переменную с именем stringvar. В этом случае Python по умолчанию считает, что это локальная переменная, если ключевое слово global уже не используется.

Однако линия

dictvar['key1'] += 1

Совершенно однозначно. Он может ссылаться только на глобальную переменную dictvar, так как dictvar уже должен существовать, чтобы оператор не выдавал ошибку.

Это не относится к словарям - то же самое верно для списков:

listvar = ["hello", "world"]

def listfoo():
    listvar[0] = "goodbye"

или другие типы объектов:

class MyClass:
    foo = 1
myclassvar = MyClass()

def myclassfoo():
    myclassvar.foo = 2

Это правда, когда используется мутирующая операция, а не перевязка.

Ответ 2

Вы можете изменить любой изменяемый объект без использования ключевого слова global.

Это возможно в Python, потому что global используется, когда вы хотите переназначить новые объекты на имена переменных, уже используемые в глобальной области видимости или для определения новых глобальных переменных.

Но в случае измененных объектов вы не перенаправляете ничего, вы просто модифицируете их на месте, поэтому Python просто загружает их из глобальной области и изменяет их.

Как docs сказать:

Невозможно назначить глобальную переменную без глобального.

In [101]: dic = {}

In [102]: lis = []

In [103]: def func():
    dic['a'] = 'foo'
    lis.append('foo') # but  fails for lis += ['something']
   .....:     

In [104]: func()

In [105]: dic, lis
Out[105]: ({'a': 'foo'}, ['foo'])

dis.dis:

In [121]: dis.dis(func)
  2           0 LOAD_CONST               1 ('foo')
              3 LOAD_GLOBAL              0 (dic)     # the global object dic is loaded
              6 LOAD_CONST               2 ('a')
              9 STORE_SUBSCR                         # modify the same object

  3          10 LOAD_GLOBAL              1 (lis)    # the global object lis is loaded
             13 LOAD_ATTR                2 (append)
             16 LOAD_CONST               1 ('foo')
             19 CALL_FUNCTION            1
             22 POP_TOP             
             23 LOAD_CONST               0 (None)
             26 RETURN_VALUE