Почему глобальные переменные - зло? [закрыто]

122

Я пытаюсь выяснить, почему использование globalв Python (и в программировании в целом) считается плохой практикой. Кто-нибудь может объяснить? Также приветствуются ссылки с дополнительной информацией.

LarsVegas
источник
Голосование за повторное открытие - я отредактировал вопрос, чтобы сместить акцент на объяснения, а не на внешние ресурсы. (Я предполагаю , что это , почему она была закрыта, но только в том случае , если это что - то делать с бытием вопроса о плохой практике сравнить эти другие вопросы о плохой практике , которые все еще открыты: eval, import *, конкатенация , переменномid , тень атрибутов )
wjandrea

Ответы:

156

Это не имеет ничего общего с Python; глобальные переменные плохо подходят для любого языка программирования.

Однако глобальные константы концептуально не то же самое, что глобальные переменные ; глобальные константы совершенно безвредны. В Python различие между ними чисто условное: CONSTANTS_ARE_CAPITALIZEDи globals_are_not.

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

Однако разумное использование глобального состояния допустимо (как и локальное состояние и изменчивость) даже в функциональном программировании, либо для оптимизации алгоритмов, уменьшения сложности, кэширования и мемоизации, либо для практичности переноса структур, происходящих из преимущественно императивной кодовой базы.

В общем, на ваш вопрос можно ответить по-разному, поэтому лучше всего просто погуглить «почему глобальные переменные плохи». Некоторые примеры:

Если вы хотите глубже понять, почему возникают побочные эффекты, а также многое другое, вам следует изучить функциональное программирование:

Эрик Каплун
источник
35

Да теоретически глобалы (и «состояние» в целом) - зло. На практике, если вы заглянете в каталог пакетов python, вы обнаружите, что большинство модулей там начинаются с набора глобальных объявлений. Очевидно, у людей с ними нет проблем.

В частности, для python видимость глобальных объектов ограничена модулем, поэтому нет «настоящих» глобальных объектов, влияющих на всю программу, что делает их менее опасными. Еще один момент: их нет const, поэтому, когда вам нужна константа, вы должны использовать глобальную.

В моей практике, если мне случится изменить глобальный объект в функции, я всегда объявляю его с помощью global, даже если технически в этом нет необходимости, например:

cache = {}

def foo(args):
    global cache

    cache[args] = ...

Это упрощает отслеживание манипуляций глобальными объектами.

Georg
источник
3
Во многих отношениях модуль в Python похож на одноэлементный класс, а глобальные переменные модуля аналогичны свойствам класса.
Corley Brigman
9
@CorleyBrigman: одиночные классы на самом деле часто страдают от тех же проблем, которые обычно приписываются глобальным переменным :)
Эрик Каплун
4
видимость модулей Python не ограничивается модулем. Они доступны во всем интерпретаторе, и (вот что важно) изменения там влияют на весь интерпретатор. Это не похоже на строку, которую вы создаете экземпляры ... это похоже на изменение всех экземпляров строки. Запах лоскута обезьяны.
graffic
2
Большинство модулей не начинают с определения глобальных переменных, кроме констант. Глобальные переменные - это плохо, это означает, что переменные / глобальное состояние не являются константами.
BlackJack
2
Использование глобальных переменных - ужасная идея, одной из причин может быть неспособность правильно протестировать функции, обновляющие некоторый произвольный словарь, который существует «где-то». Кодовая база с глобальными объектами не может быть фактически функциональна.
Tomasz Sosiński
10

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

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

(Вероятно) Нарушение определения чистой функции

Я считаю, что чистый и (почти) безошибочный код должен иметь максимально чистые функции (см. Чистые функции ). Чистая функция - это функция, удовлетворяющая следующим условиям:

  1. Функция всегда оценивает одно и то же значение результата при одном и том же значении аргумента . Значение результата функции не может зависеть от какой-либо скрытой информации или состояния, которое может измениться во время выполнения программы или между различными выполнениями программы, а также не может зависеть от какого-либо внешнего ввода от устройств ввода-вывода (обычно - см. Ниже).
  2. Оценка результата не вызывает никаких семантически наблюдаемых побочных эффектов или выходных данных , таких как изменение изменяемых объектов или выход на устройства ввода-вывода.

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

Другое четкое определение чистых функций: «Чистая функция - это функция, которая принимает все свои входные данные как явные аргументы и производит все свои выходные данные как явные результаты ». [1] . Наличие глобальных переменных нарушает идею чистых функций, поскольку вход и, возможно, один из выходов (глобальная переменная) явно не передаются или не возвращаются.

(Вероятно) Нарушение принципа ПЕРВОГО модульного тестирования

Далее , что, если вы считаете , юнит-тестирование и первый принцип ( F Аст испытания, I ndependent тесты, R epeatable, S эльфа-Validating и T imely) , вероятно , будет нарушать принцип независимых тестов (что означает , что тесты не зависят друг на друга).

Наличие глобальной переменной (не всегда), но в большинстве случаев (по крайней мере, из того, что я видел до сих пор) - это подготовка и передача результатов другим функциям. Это также нарушает этот принцип. Если глобальная переменная использовалась таким образом (т. Е. Глобальная переменная, используемая в функции X, должна быть сначала установлена ​​в функции Y), это означает, что для функции модульного тестирования X вы должны сначала запустить функцию тестирования / запуска Y.

Глобалы как константы

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

Я не считаю, что использовать глобальные переменные нельзя. Но если они используются, авторам следует рассмотреть некоторые принципы (возможно, упомянутые выше, а также другие принципы и передовые методы разработки программного обеспечения) для более чистого и почти безошибочного кода.

Рафаэль
источник
1
Мне нравится «глобальные переменные, поскольку константы - это проблема» ... потому что, если вы занимаетесь объектно-ориентированным дизайном ... это действительно так. Зачем кому-либо, кроме класса IdCreator, необходимо знать ID_LEN?
Эрик Аронести 08
3

Они необходимы, и экран тому хороший тому пример. Однако в многопоточной среде или с участием большого количества разработчиков на практике часто возникает вопрос: кто (ошибочно) установил или очистил его? В зависимости от архитектуры анализ может быть дорогостоящим и часто требоваться. Хотя чтение глобальной переменной может быть нормальным, запись в нее должна контролироваться, например, одним потоком или потокобезопасным классом. Следовательно, глобальные вары вызывают страх перед высокими затратами на разработку, возможными из-за последствий, которые сами по себе считаются злом. Поэтому в целом рекомендуется поддерживать низкое количество глобальных переменных.

Хорст Шлавуцке
источник