Сумасбродство глобальной переменной Python

У вас есть три файла: main.py, second.py и common.py

common.py

#!/usr/bin/python GLOBAL_ONE = "Frank" 

main.py

 #!/usr/bin/python from common import * from second import secondTest if __name__ == "__main__": global GLOBAL_ONE print GLOBAL_ONE #Prints "Frank" GLOBAL_ONE = "Bob" print GLOBAL_ONE #Prints "Bob" secondTest() print GLOBAL_ONE #Prints "Bob" 

second.py

 #!/usr/bin/python from common import * def secondTest(): global GLOBAL_ONE print GLOBAL_ONE #Prints "Frank" 

Почему secondTest не использует глобальные переменные своей вызывающей программы? Какой смысл называть что-то «глобальным», если на самом деле это не так !?

Что мне не хватает, чтобы получить secondTest (или любую внешнюю функцию, которую я вызываю из главной), чтобы распознавать и использовать правильные переменные?

global означает глобальный для этого модуля, а не для всей программы. Когда вы это сделаете

 from lala import * 

вы добавляете все определения lala как locals в этот модуль .

Итак, в вашем случае вы получаете две копии GLOBAL_ONE

Первый и очевидный вопрос – почему?

Есть несколько ситуаций, когда глобальные переменные необходимы / полезны, но их действительно мало.

Ваша проблема связана с пространствами имен. Когда вы импортируете common в second.py, GLOBAL_ONE происходит из этого пространства имен. Когда вы импортируете secondTest, он по-прежнему ссылается на GLOBAL_ONE из common.py.

Ваша настоящая проблема, однако, связана с дизайном. Я не могу придумать ни единой логической причины для реализации глобальной переменной таким образом. Глобальные переменные – сложный бизнес в Python, потому что нет такой вещи, как постоянная переменная. Тем не менее, соглашение заключается в том, что если вы хотите сохранить что-то постоянное в Python, вы WITH_ALL_CAPS его WITH_ALL_CAPS . Ergo:

 somevar = MY_GLOBAL_VAR # good! MY_GLOBAL_VAR = somevar # What? You "can't" assign to a constant! Bad! 

Существует множество причин, которые делают что-то вроде этого:

 earth = 6e24 def badfunction(): global earth earth += 1e5 print '%.2e' % earth 

ужасно.

Конечно, если вы просто делаете это как упражнение в понимании пространств имен и global вызова, продолжайте.

Если нет, некоторые из причин, по которым глобальными переменными являются A Bad Thing ™, являются:

  • Загрязнение пространства имен
  • Функциональная интеграция – вы хотите, чтобы ваши функции были разделены на части
  • Функциональные побочные эффекты – что происходит, когда вы пишете функцию, которая изменяет balance глобальной переменной, и либо вы, либо кто-то другой повторно используете вашу функцию и не учитываете это? Если вы рассчитываете баланс счета, внезапно у вас слишком много или недостаточно. Такие ошибки трудно найти.

Если у вас есть функция, которая нуждается в значении, вы должны передать это значение в качестве параметра, если у вас нет по-настоящему веской причины. Одна из причин заключалась бы в глобальном PI – в зависимости от ваших потребностей в точности, вы можете захотеть, чтобы она была 3.14 , или вам может понадобиться ее 3.14159265 … но это тот случай, когда глобальность имеет смысл. Вероятно, есть только несколько или два реальных случая, которые могут правильно использовать глобальные переменные. Один из случаев – это константы в программировании игр. Легче импортировать pygame.locals и использовать KP_UP, чем помнить целочисленное значение, отвечающее за это событие. Это исключения из правила.

И (по крайней мере, в pygame) эти константы хранятся в отдельном файле – только для констант. Любой модуль, который нуждается в этих константах, будет импортировать указанные константы.

Когда вы программируете, вы пишете функции, чтобы разбить проблему на управляемые куски. Предпочтительно функция должна делать одно и не иметь побочных эффектов. Это означает, что функция, такая как calculatetime (), должна вычислять время. Вероятно, он не должен читать файл, содержащий время, и запрещать ему делать что-то вроде записи времени где-то. Он может вернуть время и принимать параметры, если он им нужен – оба из них – хорошие, приемлемые вещи для выполнения функций. Функции – это своего рода контракт между вами (программистом функции) и любым (включая вас), который использует эту функцию. Доступ и изменение глобальных переменных являются нарушением этого контракта, поскольку функция может изменять внешние данные способами, которые не определены или не ожидаются. Когда я использую эту функцию calculatetime() , я ожидаю, что она вычислит время и, возможно, вернет его, а не изменит глобальное время переменной, которое отвечает на время модуля, которое я только что импортировал.

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

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

common.py

 #!/usr/bin/python GLOBAL_ONE = "Frank" 

main.py

 #!/usr/bin/python from second import secondTest import common if __name__ == "__main__": print common.GLOBAL_ONE # Prints "Frank" common.GLOBAL_ONE = "Bob" print common.GLOBAL_ONE # Prints "Bob" secondTest() print common.GLOBAL_ONE # Prints "Bob" 

second.py

 #!/usr/bin/python import common def secondTest(): print common.GLOBAL_ONE # Prints "Bob" 

Позвольте мне сначала сказать, что я согласен со всеми, кто ответил, прежде чем сказать, что это, вероятно, не то, что вы хотите сделать. Но в случае, если вы действительно уверены, что это путь, вы можете сделать следующее. Вместо того, чтобы определять GLOBAL_ONE как строку в common.py , определите ее как список, то есть GLOBAL_ONE = ["Frank"] . Затем вы читаете и изменяете GLOBAL_ONE[0] вместо GLOBAL_ONE и все работает так, как вы хотите. Обратите внимание: я не думаю, что это хороший стиль, и, вероятно, есть лучшие способы добиться того, чего вы действительно хотите.