Подтвердить что ты не робот

Python: мышление модуля и его переменных как одноэлементный подход - чистый подход?

Я хотел бы реализовать какой-то одноэлементный шаблон в моей программе Python. Я думал об этом без использования классов; то есть я хотел бы поместить все связанные с singleton функции и переменные в модуль и считать его реальным синглетоном.

Например, скажем, что это должно быть в файле 'singleton_module.py':

# singleton_module.py

# Singleton-related variables
foo = 'blah'
bar = 'stuff'

# Functions that process the above variables
def work(some_parameter):
    global foo, bar
    if some_parameter:
        bar = ...
    else:
        foo = ...

Тогда остальная часть программы (т.е. другие модули) будет использовать этот синглтон так:

# another_module.py

import singleton_module

# process the singleton variables,
# which changes them across the entire program
singleton_module.work(...)

# freely access the singleton variables
# (at least for reading)
print singleton_module.foo

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

Однако все эти утомительные "глобальные" утверждения в модуле singleton уродливы. Они встречаются в каждой функции, которая обрабатывает одноэлементные переменные. Это не так много в этом конкретном примере, но когда у вас есть 10+ переменных для управления несколькими функциями, это не очень.

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

Итак, будет ли это считаться чистым? Есть ли подход, похожий на мой, который справляется с "глобальным" беспорядком?

Или это просто не путь?

4b9b3361

Ответ 1

Общей альтернативой использованию модуля в качестве синглета является Alex Martelli Borg pattern:

class Borg:
    __shared_state = {}
    def __init__(self):
        self.__dict__ = self.__shared_state
    # and whatever else you want in your class -- that all!

Может быть несколько экземпляров этого класса, но все они имеют одно и то же состояние.

Ответ 2

Возможно, вы можете поместить все переменные в глобальный dict, и вы можете напрямую использовать dict в своих функциях без "глобального".

# Singleton-related variables
my_globals = {'foo': 'blah', 'bar':'stuff'}

# Functions that process the above variables
def work(some_parameter):
    if some_parameter:
        my_globals['bar'] = ...
    else:
        my_globals['foo'] = ...

зачем вы это делаете, это области и пространства имен Python.

Ответ 3

Подобно предложению Sven "Borg pattern", вы можете просто сохранить все данные состояния в классе, не создавая экземпляров класса. Я полагаю, этот метод использует классы нового стиля.

Этот метод может быть даже адаптирован к шаблону Borg, с оговоркой, что изменение членов состояния из экземпляров класса потребует доступа к атрибуту __class__ экземпляра (instance.__class__.foo = 'z', а не instance.foo = 'z', хотя вы также можете просто сделать stateclass.foo = 'z').

class State:    # in some versions of Python, may need to be "class State():" or "class State(object):"
    __slots__ = []   # prevents additional attributes from being added to instances and same-named attributes from shadowing the class attributes
    foo = 'x'
    bar = 'y'

    @classmethod
    def work(cls, spam):
        print(cls.foo, spam, cls.bar)

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

Ответ 4

Один подход к реализации одноэлементного паттерна с Python также может быть:

метод singleton __init()__ вызывает исключение, если экземпляр класса уже существует. Точнее, класс имеет член _single. Если этот член отличается от None, возникает исключение.

class Singleton:
    __single = None
    def __init__( self ):
        if Singleton.__single:
            raise Singleton.__single
        Singleton.__single = self  

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

def Handle( x = Singleton ):
    try:
        single = x()
    except Singleton, s:
        single = s
    return single 

этот метод handle() очень похож на то, что было бы реализацией С++ для шаблона Singleton. Мы могли бы иметь в классе Singleton handle()

Singleton& Singleton::Handle() {

  if( !psingle ) {
    psingle = new Singleton;
  }
  return *psingle;
}

возвращает либо новый экземпляр Singleton, либо ссылку на существующий уникальный экземпляр класса Singleton.

Обработка всей иерархии

Если классы Single1 и Single2 вытекают из Singleton, существует один экземпляр Singleton через один из производного класса. Это можно проверить следующим образом:

>>> child = S2( 'singlething' )
>>> junior = Handle( S1)
>>> junior.name()
'singlething'

Ответ 5

Создание WillYang ответьте и сделайте еще один шаг к чистоте: определите простой класс для хранения вашего глобального словаря, чтобы упростить ссылку:

class struct(dict):
    def __init__(self, **kwargs):
        dict.__init__(self, kwargs)
        self.__dict__ = self

g = struct(var1=None, var2=None)

def func():
    g.var1 = dict()
    g.var3 = 10
    g["var4"] = [1, 2]
    print(g["var3"])
    print(g.var4)

Точно так же, как перед тем, как вы поместите что-нибудь, что хотите, в g, но теперь оно очень чистое.:)

Ответ 6

Для законного Singleton:

class SingletonMeta(type):
    __classes = {} # protect against defining class with the same name
    def __new__(cls, cls_name, cls_ancestors, cls_dict): 
         if cls_name in cls.__classes:
             return cls.__classes[cls_name]
         type_instance = super(SingletonMeta, cls).__new__(cls, cls_name, cls_ancestors, cls_dict) # pass 'type' instead of 'cls' if you dont want SingletonMeta attributes reflected in the class
         return type_instance() # call __init__

class Singleton:
    __metaclass__ = SingletonMeta
    # define __init__ however you want

    __call__(self, *args, *kwargs):
        print 'hi!'

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

singleton = Singleton() # prints "hi!"