Использование модуля как синглтона в Python - это нормально? - PullRequest
0 голосов
/ 22 октября 2018

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

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

// Пример минимального, полного и проверяемого:

"""

This is __init__.py of the module, that could be used as a singleton:

I need to set and get value of IMPORTANT_VARIABLE from different places in my code.

Folder structure:
--singleton_module
 |
 -__init__.py

Example of usage:
import singleton_module as my_singleton
my_singleton.set_important_variable(3)
print(my_singleton.get_important_variable())

"""

IMPORTANT_VARIABLE = 0

def set_important_variable(value):
    global IMPORTANT_VARIABLE
    IMPORTANT_VARIABLE = value

def get_important_variable():
    return IMPORTANT_VARIABLE

1 Ответ

0 голосов
/ 22 октября 2018

Технически, модули Python являются одиночными, поэтому с этой точки зрения нет особых проблем (кроме обычных проблем с одиночками, которые есть) с вашим кодом.Я бы просто записал varibale в all_lower (ALL_UPPER обозначает псевдоконстанту) и поставил перед ним префикс с одинарным («защищенным») или двойным («действительно частным»), чтобы было ясно, что он не является частью общедоступного API (стандартное соглашение об именах Python).

Теперь вопрос о том, являются ли синглтоны хорошей идеей, является еще одной дискуссией, но здесь дело не в этом ...

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

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

src/
  foo/
    __init.py
    bar/
      __init__.py
      baaz/
         __init__.py
         mymodule.py

с "src" и "foo" в sys.path, затем импортирует mymodule один раз как from foo.bar.baaz import mymodule и второй раз как from bar.baaz import mymodule

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

Кроме того, я не уверен, как бы использовать объект вместо модуляповысить безопасность

Это не так.

И я просто спрашиваю, если это не плохая практика, может быть, кто-то сделал это и обнаружил некоторые проблемы.Это, вероятно, не популярный шаблон

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

Теперь вы не получите все приятные функции OO, такие как вычисляемые атрибуты,наследование, магические методы и т. д., но я предполагаю, что вы уже поняли это.

Насколько мне известно, в зависимости от контекста, я мог бы скорее использовать простой класс, но выставить только один единственный экземпляр класса в качествеAPI модуля, например:

# mymodule.py

__all__ = ["mysingleton"]

class __MySingletonLike(object):
    def __init__(self):
        self._variable = 42

    @property
    def variable(self):
        return self._variable

    @variable.setter
    def variable(self, value):
        check_value(value) # imaginary validation
        self._variable = value


mysingleton = __MySingleton()

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

...