Python — оптимальный способ переназначения глобальных переменных из функции в другом модуле

#python #import #module #global-variables

#python #импорт #модуль #глобальные переменные

Вопрос:

У меня есть модуль, который я вызвал entities.py — внутри него есть 2 класса и 2 глобальные переменные, как показано ниже.:

 FIRST_VAR = ...
SECOND_VAR = ...

class FirstClass:
    [...]

class SecondClass:
    [...]
  

У меня также есть другой модуль (назовем его main.py на данный момент), где я импортирую оба класса и константы, как здесь:

 from entities import FirstClass, SecondClass, FIRST_VAR, SECOND_VAR
  

В том же «main.py » модуль У меня есть другая константа: THIRD_VAR = ... , и другой класс, в котором используются все импортированные имена.

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

 def update_consts_from_config(config: ConfigParser):
    global FIRST_VAR
    global SECOND_VAR
    global THIRD_VAR
    FIRST_VAR = ...
    SECOND_VAR = ...
    THIRD_VAR = ...
  

Это работает отлично, хотя PyCharm указывает на две проблемы, которые, по крайней мере, я не считаю точными.

from entities import FirstClass, SecondClass, FIRST_VAR, SECOND_VAR — здесь он предупреждает меня, что FIRST_VAR и SECOND_VAR являются неиспользуемыми импортами, но, насколько я понимаю и тестирую, они используются и не объявляются повторно в другом месте, если update_consts_from_config не вызывается функция.

Кроме того, в разделе update_consts_from_config функция:

global FIRST_VAR — в этой и следующей строке указано, что глобальная переменная FIRST_VAR не определена на уровне модуля

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

Я знаю, что могу сделать что-то как:

 import entities
from entities import FirstClass, SecondClass

FIRST_VAR = entities.FIRST_VAR
SECOND_VAR = entities.SECOND_VAR
  

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

Как вы думаете, что было бы лучшей практикой здесь? Я бы хотел, чтобы мой код был четким, однозначным и каким-то образом оптимальным.

Комментарии:

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

2. Мне кажется, что global FIRST_VAR вместо изменения переменной из импортированного документа создается новая переменная. Вы пробовали печатать FIRST_VAR , чтобы проверить это? Возможно, вам следует подумать о том, чтобы либо сделать эти переменные статическими переменными класса, либо использовать entities.FIRST_VAR , как вы упомянули. Я не уверен, может ли python вызывать переменную из другого модуля в качестве глобальной переменной.

3. Если вы настаиваете на использовании глобальных переменных, вам следует использовать ‘entities. FIRST_VAR’ везде в main.py

4. @quamrana Спасибо за комментарий. Я знаю об этом, я не использую глобальные переменные на регулярной основе, для меня просто имело смысл объявить их в этой самой ситуации как глобальные переменные таким же образом, как pi объявлено как глобальная переменная в модуле Math.

5. @FlavioMoraes Да, я пробовал это, во время отладки FIRST_VAR появился так, как когда он был объявлен как в функции, так и в основном фрейме, и после выполнения переназначения он изменился как в фрейме функции, так и в основном фрейме, как я и ожидал. «Я не уверен, может ли python вызывать переменную из другого модуля в качестве глобальной переменной» — это сложная часть, в которой я тоже не совсем уверен. В любом случае, спасибо, я могу использовать сущности. FIRST_VAR, если это имеет больше смысла

Ответ №1:

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

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

main.py:

 import entities

def update_consts_from_config(FIRST_VAR):
    entities.FIRST_VAR = FIRST_VAR

firstclass = entities.FirstClass()

print(f"{entities.FIRST_VAR=} before override")
firstclass.debug()
entities.debug()

update_consts_from_config("override")

print(f"{entities.FIRST_VAR=} after override")
firstclass.debug()
entities.debug()
  

entities.py:

 FIRST_VAR = "ori"

class FirstClass:
    def debug(self):
        print(f"entities.py:{FIRST_VAR=}")


def debug():
    print(f"making sure no closure/locality effects after object instantation {FIRST_VAR=}")
  

$ python main.py

 
entities.FIRST_VAR='ori' before override
entities.py:FIRST_VAR='ori'
making sure no closure/locality effects after object instantation FIRST_VAR='ori'
entities.FIRST_VAR='override' after override
entities.py:FIRST_VAR='override'
making sure no closure/locality effects after object instantation FIRST_VAR='override'
  

Теперь, если FIRST_VAR не был строкой, int или другим типом неизменяемого, я думаю, вы должны иметь возможность импортировать его отдельно и изменять его. Как SECOND_VAR.append("config override") в main.py . Но присвоение глобальному в main.py повлияет только на main.py привязка, поэтому, если вы хотите разделить фактическое состояние между main.py и сущности, и другие модули, все, а не только import entities main.py затем необходимо получить доступ entities.FIRST_VAR .

О, и если бы у вас:

 class SecondClass:

   def __init__(self):
      self.FIRST_VAR = FIRST_VAR
  

тогда на его значение уровня экземпляра этой неизменяемой строки / int не будут влиять какие-либо переопределения, выполненные после создания экземпляра. Это повлияет на изменяемые файлы, такие как списки или словари, поскольку все они представляют собой разные привязки, указывающие на одну и ту же переменную.

Наконец, обратитесь к этим «сложным» пространствам имен. global в вашем исходном коде означает: «не рассматривайте FIRST_VAR как переменную для назначения в update_consts_from_config локальном пространстве имен s, вместо этого назначьте ее main.py глобальное пространство имен на уровне сценариев».

Это не означает «присвоить его какому-то глобальному состоянию, волшебным образом разделяемому между entities.py и main.py «. __builtins__ может быть, это и есть тот зверь, но его модификация считается крайне дурным тоном в Python.

Комментарии:

1. Спасибо JL за объяснение. Наконец, я изменил код так, чтобы он использовал статические атрибуты класса, а не глобальные переменные. Я знал, что работаю над пространством имен на уровне сценариев, но благодаря вашему ответу я понял, что назначение, которое я написал, могло быть неоднозначным, если бы другие люди использовали код. Если бы я должен был изменить константу, я должен был бы сделать это действительно глобально, тогда как присвоения значения новой переменной и ее изменения в скрипте должно быть достаточно для локальной цели скрипта.

2. «глобальные» переменные, будь то на уровне модуля, переменные статического класса или даже синглтоны, которые некоторые люди, не без оснований, отвергают как прославленные глобальные переменные, — это одно. Дело в том, что для конфигурации, ну, вы вроде как должны получить информацию в одном месте. Однако, в частности global , ключевое слово в функциях, а не общая нечеткая концепция глобальных переменных, по крайней мере, в моем коде, всегда оказывалось чем-то вроде кодового запаха.