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+ переменных для управления несколькими функциями, это не очень.
Кроме того, это довольно подвержено ошибкам, если вы забудете глобальные операторы: будут созданы переменные, локальные для этой функции, и переменные модуля не будут изменены, что вам не нужно!
Итак, будет ли это считаться чистым? Есть ли подход, похожий на мой, который справляется с "глобальным" беспорядком?
Или это просто не путь?
Ответы
Ответ 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!"