В настоящее время я работаю над проектом, в котором у меня есть действительно большое количество глобальных констант, которые нужно жестко запрограммировать в моем коде. Проект написан на Python. И, как мы все знаем, производительность Python, когда дело доходит до доступа к глобальным переменным, значительно падает.
Я мог бы переместить те константы, которые я использую только в одном методе, в локальную область методов, из которых я их использую, но это снижает удобочитаемость. Тогда у меня все еще будут некоторые глобальные объекты, которые используются в нескольких методах, и я действительно не могу скрыть их в рамках только одной функции.
Что с этим делать? Я видел парня, создающего игру (Ludum Dare 31), и, например, в 3:30 вы можете видеть, что у него есть только один большой файл constants.py с чертовски большим количеством глобальных переменных (без ключевого слова global). Это хорошая практика?
«И, как мы все знаем, производительность Python, когда дело доходит до доступа к глобальным переменным, значительно падает» - действительно ли это серьезное узкое место для вашего кода?
В большинстве случаев вам следует поместить все глобальные переменные в один файл. Это увеличивает удобочитаемость и экономит много работы с поддержкой / обновлениями. Если проблема заключается в более медленной загрузке глобальных объектов, вам, вероятно, следует подумать об использовании более оптимизированных языков для вашего кода.
Думаю, это не узкое место. Потому что я предполагаю, что глобальные объекты будут доступны только один или два раза. Они не похожи на i для цикла итерации 10^6. Думаю, я мог бы оставить их все в отдельном файле.
@AlexK. Я прошу не согласиться (по крайней мере, частично) - это зависит от того, как используются эти константы и должны ли они быть редактируемыми пользователем (конфигурация) или нет (реальные константы, такие как значение числа пи).
@Frynio, и если вам нужно использовать заданную константу в жестком цикле, вы всегда можете использовать псевдоним для нее локально.
«преждевременная оптимизация - корень всех зол», поэтому сначала подумайте с точки зрения удобочитаемости. Кроме того, мы очень плохо угадываем истинные узкие места, поэтому, если у вас есть проблемы с производительностью, сначала убедитесь, что вы запускаете свой код под профилировщиком, прежде чем предпринимать какие-либо действия.
@brunodesthuilliers Значит, мне нужно только один раз сослаться на глобальный, а потом я на него ссылаюсь как на локальный?
@Frynio: mylocal = myglobal перед циклом, затем используйте mylocal в цикле. Таким образом, глобальный поиск выполняется только один раз. Это, конечно, требует, чтобы это было в той же функции - если жесткий цикл вызывает функцию, которая использует глобальную, тогда она, очевидно, не будет работать так, как ожидалось ;-)






Если все, что вас волнует, это производительность вашего кода в поиске глобального пространства имен, вы, вероятно, можете сделать
globals()['your_constant_name'] # inside your function/method
который будет напрямую искать вещи в глобальном пространстве имен. Обратите внимание: если по какой-то причине константа не существует, то вместо AttributeError будет сгенерирована ошибка KeyError.
Кроме того, согласно документации Python
This is always the dictionary of the current module (inside a function or method, this is the module where it is defined, not the module from which it is called)
Так что используйте его с осторожностью.
Обновлено:
Это немного крайний случай (вряд ли произойдет в любом реалистичном сценарии), но поиск по словарю немного увеличивает производительность, если кадр стека огромен, несмотря на построение словаря и т. д., Как упоминал @brunodesthuilliers. Код тестирования:
import itertools,timeit
globals().update({''.join(n):i for i,n in enumerate(itertools.permutations('ABCDEFGHI'))})
def with_dict():
def func():
try:
func()
except RecursionError:
globals()['ABCDEFGHI']
def without_dict():
def func():
try:
func()
except RecursionError:
ABCDEFGHI
print(timeit.timeit(with_dict)) # output: 0.33404375400277786
print(timeit.timeit(without_dict)) # output: 0.3390919269877486
Хотя, согласно python вики, поиск по словарю имеет среднюю временную сложность O (1)
Здесь вам вообще не нужен globals, и это улучшит производительность определенно нет - у вас все еще есть затраты на глобальный поиск по имени globals, затем вызов функции, построение dict и, наконец, поиск dict). Вы можете использовать dis.dis(), чтобы узнать, как выглядят соответствующие байт-коды, и timeit.timeit(), чтобы сравнить эффективные характеристики.
@brunodesthuilliers Я обновил свой ответ. Вы частично правы.
as we all know, Python performance when it comes to accessing global variables drops down significantly.
Не «значительно» - локальные поиски действительно немного дешевле, но определение локальной переменной также требует затрат, поэтому, если вы не ищете глобальную переменную в очень узком цикле, шансы, что вы когда-либо заметите разницу, действительно невелики, а затем вы всегда можете использовать псевдоним global локально, то есть из:
FOO = 42
def foo():
for i in range(100000000):
x = i * FOO
к
FOO = 42
def foo():
localfoo = FOO
for i in range(100000000):
x = i * localfoo
Другими словами, вам действительно не следует беспокоиться о проблемах с производительностью здесь, пока производительность не станет реальной проблемой, и профилировщик определит этот глобальный поиск как основное узкое место (что действительно очень маловероятно), и даже тогда я серьезно сомневаюсь, что вы когда-либо получить какой-либо значительный прирост в конце - если стоимость глобального поиска уже слишком высока для вашего приложения, тогда Python не является подходящим инструментом, и пора переписать эту часть на C.
I could move those constants, that I use only in one method, to the local scope of the methods I use them from, but that removes readability.
И, как упоминалось выше, не обязательно улучшит производительность:
>>> import dis
>>> import timeit
>>>
>>> FOO = 42
>>> def foo1():
... return FOO
...
>>> def foo3():
... foo = 42
... return foo
...
>>> dis.dis(foo1)
2 0 LOAD_GLOBAL 0 (FOO)
3 RETURN_VALUE
>>>
>>> dis.dis(foo3
... )
2 0 LOAD_CONST 1 (42)
3 STORE_FAST 0 (foo)
3 6 LOAD_FAST 0 (foo)
9 RETURN_VALUE
>>> timeit.timeit("func()", "from __main__ import foo1 as func")
0.06334185600280762
>>> timeit.timeit("func()", "from __main__ import foo3 as func")
0.06805109977722168
Then I will still have some globals which are used in multiple methods and I really cannot hide them in the scope of only one function. What's the solution to that?
В чем собственно проблема?
I saw a guy making a game (...) you can see that he just has one big file constants.py with a hell lotta global variables there (without global keyword).
Все имена, определенные на верхнем уровне модуля (путем присвоения, импорта, определения функции или определения класса), являются «глобальными» для модуля (и это единственный вид «глобальных», который вы найдете в Python - там НЕТ "глобальные объекты для всего приложения"). Ключевое слово global должно использоваться только внутри функций и только тогда, когда вы действительно хотите назначить этот глобальный объект внутри функции - то, что мы все знаем, что НЕ должны делать, не так ли?
Is this a good practic?
Зависит от того, как и где используются эти «константы». Если у вас есть константы, которые используются более чем одним модулем, И нет других зависимостей между этими модулями, тогда это действительно имеет смысл, но большую часть времени константы либо используются только одним модулем, либо другим модулям, использующим их, также нужны другие имена (функции, классы и т. д.) из одного модуля.
Короче говоря: в константах нет ничего особенного, это просто имена, ссылающиеся на объекты (вы можете не осознавать, но все ваши функции и классы тоже ЯВЛЯЮТСЯ «константами»), поэтому вы просто хотите применить те же правила, что и для всего остального: ваши модули должны иметь сильную сплоченность (все в модуле сильно взаимосвязано) и низкую связанность (ваш модуль зависит от как можно меньшего числа других модулей). С этой точки зрения, определение десятков несвязанных констант в одном файле, от которого зависят более 10 других несвязанных модулей, просто неправильно - это нарушает целостность и вводит сильную связь.
Обратите внимание, что у вас может быть другая причина для «централизации» констант (по крайней мере, некоторых из них) таким образом: упрощение настройки - но это применимо только к константам, которые вы хотите сделать настраиваемыми (вы бы сделали настраиваемым значение «pi»? ), и это совершенно другой вопрос.
Что ж, теперь, кажется, все ясно. Еще раз спасибо, это мне очень помогло
Какие данные будут в этих константах?