Python: думать о модуле и его переменных как об одиночном. Чистый подход? - PullRequest
31 голосов
/ 06 июня 2011

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

Например, скажем, это должно быть в файле '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

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

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

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

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

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

Ответы [ 6 ]

22 голосов
/ 06 июня 2011

Обычной альтернативой использованию модуля в качестве одиночного является шаблон Алекса Мартелли :

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

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

4 голосов
/ 27 февраля 2013

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

# 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 .

1 голос
/ 20 мая 2013

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

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

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

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

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

, этот метод Handle() очень похож на то, что было бы реализацией C ++ шаблона 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'
0 голосов
/ 24 октября 2017

Для законного 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's 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!"
0 голосов
/ 19 января 2017

Построение ответа 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, но теперь это очень чисто.:)

0 голосов
/ 06 июня 2011

Как и в случае с предложением Свена о "шаблоне Борг", вы можете просто хранить все данные о состоянии в классе, не создавая никаких экземпляров класса. Я полагаю, что этот метод использует классы нового стиля.

Этот метод может быть даже адаптирован к шаблону Борг, с оговоркой, что изменение членов состояния из экземпляров класса потребует доступа к атрибуту __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's attributes
    foo = 'x'
    bar = 'y'

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

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

...