Попробуйте следующее:
punct(u(a(t(i(o(n)?)?)?)?)?)?
Код на основе Ответ Tolli .
#decorator, modyfies new_cls
def _singleton(new_cls):
instance = new_cls() #2
def new(cls):
if isinstance(instance, cls): #4
return instance
else:
raise TypeError("I can only return instance of {}, caller wanted {}".format(new_cls, cls))
new_cls.__new__ = new #3
new_cls.__init__ = lambda self: None #5
return new_cls
#decorator, creates new class
def singleton(cls):
new_cls = type('singleton({})'.format(cls.__name__), (cls,), {} ) #1
return _singleton(new_cls)
#metaclass
def meta_singleton(name, bases, attrs):
new_cls = type(name, bases, attrs) #1
return _singleton(new_cls)
Объяснение:
cls
(it не изменяет cls
в случае, если кто-то хочет, например, singleton(list)
) __new__
это так просто. __new__
с использованием метода, определенного момента назад. instance
только в том случае, если это то, что ожидает абонент, в противном случае возникает TypeError
. Условие не выполняется, если кто-то пытается наследовать от украшенного класса. __new__()
возвращает экземпляр cls
, то метод __init__()
нового экземпляра будет вызван как __init__(self[, ...])
, где self - это новый экземпляр, а остальные аргументы те же, что и в __new__()
. instance
уже инициализирован, поэтому функция заменяет __init__
функцией, ничего не выполняющей. Я не могу вспомнить, где я нашел это решение, но я считаю, что это самый «элегантный» из моей не-Python-экспертной точки зрения:
class SomeSingleton(dict):
__instance__ = None
def __new__(cls, *args,**kwargs):
if SomeSingleton.__instance__ is None:
SomeSingleton.__instance__ = dict.__new__(cls)
return SomeSingleton.__instance__
def __init__(self):
pass
def some_func(self,arg):
pass
Почему мне нравится это? Нет декораторов, нет мета-классов, нет множественного наследования ... и если вы решите, что больше не хотите, чтобы это был синглтон, просто удалите метод __new__
. Поскольку я новичок в Python (и ООП в целом), я ожидаю, что кто-то задаст мне прямо, почему это ужасный подход?
__new__
. Не повторяйте себя .
– GingerPlusPlus
30 December 2014 в 18:56
*args
и **kwargs
, а затем ничего не делает с ними? Передайте их в dict.__new__
следующим образом: dict.__new__(cls, *args, **kwargs)
.
– GingerPlusPlus
31 December 2014 в 14:29
class Foo(object):
pass
some_global_variable = Foo()
Модули импортируются только один раз, все остальное переоценивает. Не используйте синглтоны и не пытайтесь использовать глобальные переменные.
s = some_global_variable; str = pickle.dumps(s); s1 = pickle.loads(str); print s is s1; # False
– dividebyzero
20 January 2012 в 13:47
is
проверяет равенство указателя. Я был бы весьма удивлен - до того, чтобы называть его ошибкой - если pickle.loads
вернул ссылку на ранее существовавший объект, а не ссылку на вновь созданный. Таким образом, тестирование того, что s is s1
ничего не говорит о пригодности использования модулей в качестве однотонных.
– Jonas Kölker
17 February 2014 в 12:52
pickle.loads()
делает это уже, например. для экземпляров bool
и NoneType
. pickle.loads(pickle.dumps(False)) is False
дает True
– Dan Passaro
28 April 2014 в 18:16
True
, False
и None
и не имеет никакого отношения к коду, стоящему за pickle.loads
. Кроме того, это безопасно делать только для объектов только для чтения. Если pickle.loads
должен был возвратить ссылку на уже существующий объект modifiable i>, такой как модуль, который был бы ошибкой. (И поэтому я уверен, что пример кода dividebyzero ничего не доказывает).
– Jonas Kölker
3 May 2014 в 01:18
Я подброшу на ринг. Это простой декоратор.
from abc import ABC
def singleton(real_cls):
class SingletonFactory(ABC):
instance = None
def __new__(cls, *args, **kwargs):
if not cls.instance:
cls.instance = real_cls(*args, **kwargs)
return cls.instance
SingletonFactory.register(real_cls)
return SingletonFactory
# Usage
@singleton
class YourClass:
... # Your normal implementation, no special requirements.
Преимущества Я думаю, что он имеет некоторые из других решений:
YourClass
. Это означает, что вам не нужно использовать метакласс для вашего класса (обратите внимание, что метаклас выше находится на фабрике, а не «реальный» класс). YourClass
, он выглядит как класс (потому что это так), и они обычно используют его. Нет необходимости адаптировать вызывающих абонентов к заводской функции. То, что YourClass()
создает экземпляр, по-прежнему является истинным экземпляром YourClass
, который вы внедрили, а не прокси-сервером любого типа, поэтому никаких шансов на побочные эффекты в результате этого. isinstance(instance, YourClass)
и подобные операции все еще работают как ожидалось (хотя этот бит требует abc, поэтому исключает Python & lt; 2.6). Для меня имеет место один недостаток: classmethods и staticmethods реального класса не могут быть прозрачно вызваны с помощью класса фабрики, скрывающего его. Я использовал это достаточно редко, что мне никогда не приходилось сталкиваться с этой необходимостью, но его можно было бы легко исправить, используя специальный метакласс на заводе, который реализует __getattr__()
, чтобы делегировать доступ к атрибуту all-ish для реального класса.
Связанный шаблон, который я действительно нашел более полезным (не то, что я говорю, что такие вещи требуются очень часто вообще) является «уникальным» шаблоном, где экземпляр класса с помощью те же аргументы приводят к возврату одного и того же экземпляра. То есть «одиночный аргумент». Вышеприведенное адаптируется к этой скважине и становится еще более кратким:
def unique(real_cls):
class UniqueFactory(ABC):
@functools.lru_cache(None) # Handy for 3.2+, but use any memoization decorator you like
def __new__(cls, *args, **kwargs):
return real_cls(*args, **kwargs)
UniqueFactory.register(real_cls)
return UniqueFactory
Все, что сказал, я согласен с общим советом, что, если вы считаете, что вам нужна одна из этих вещей, вам действительно стоит остановиться на и спросите себя, действительно ли вы это делаете. 99% времени, YAGNI.
Это мой предпочтительный способ реализации синглетонов:
class Test(object):
obj = None
def __init__(self):
if Test.obj is not None:
raise Exception('A Test Singleton instance already exists')
# Initialization code here
@classmethod
def get_instance(cls):
if cls.obj is None:
cls.obj = Test()
return cls.obj
@classmethod
def custom_method(cls):
obj = cls.get_instance()
# Custom Code here
Это решение вызывает некоторое загрязнение пространства имен на уровне модуля (три определения, а не только одно), но мне легко следовать.
Я хотел бы написать что-то вроде этого ( ленивая инициализация), но, к сожалению, классы не доступны в теле их собственных определений.
# wouldn't it be nice if we could do this?
class Foo(object):
instance = None
def __new__(cls):
if cls.instance is None:
cls.instance = object()
cls.instance.__class__ = Foo
return cls.instance
Так как это невозможно, мы можем разбить инициализацию и статический экземпляр в
Ожидаемая инициализация:
import random
class FooMaker(object):
def __init__(self, *args):
self._count = random.random()
self._args = args
class Foo(object):
def __new__(self):
return foo_instance
foo_instance = FooMaker()
foo_instance.__class__ = Foo
Ленивая инициализация:
Ижевая инициализация:
import random
class FooMaker(object):
def __init__(self, *args):
self._count = random.random()
self._args = args
class Foo(object):
def __new__(self):
global foo_instance
if foo_instance is None:
foo_instance = FooMaker()
return foo_instance
foo_instance = None
Ну, не соглашаясь с общим предложением Питонов о глобальном уровне модуля, как насчет этого:
def singleton(class_):
class class_w(class_):
_instance = None
def __new__(class2, *args, **kwargs):
if class_w._instance is None:
class_w._instance = super(class_w, class2).__new__(class2, *args, **kwargs)
class_w._instance._sealed = False
return class_w._instance
def __init__(self, *args, **kwargs):
if self._sealed:
return
super(class_w, self).__init__(*args, **kwargs)
self._sealed = True
class_w.__name__ = class_.__name__
return class_w
@singleton
class MyClass(object):
def __init__(self, text):
print text
@classmethod
def name(class_):
print class_.__name__
x = MyClass(111)
x.name()
y = MyClass(222)
print id(x) == id(y)
Выход:
111 # the __init__ is called only on the 1st time
MyClass # the __name__ is preserved
True # this is actually the same instance
_sealed
? Насколько я вижу, это ничего не делает? Что-то меня обманывает, что говорит, что он не должен хорошо работать ... Я проведу несколько сравнительных тестов на этой неделе.
– theheadofabroom
25 July 2011 в 11:32
__init__
для вызова каждый раз, когда он инициализируется. Простое «Бытие инициализировано в классе.method». как для отступов - вы использовали вкладки и пробелы - я исправил большую часть из них, но, похоже, пропустил один, если вы хотите его получить (просто проверьте журнал изменений)
– theheadofabroom
26 July 2011 в 07:33
Вот вам один лайнер:
singleton = lambda c: c()
Вот как вы его используете:
@singleton
class wat(object):
def __init__(self): self.x = 1
def get_x(self): return self.x
assert wat.get_x() == 1
Ваш объект получает экземпляр с нетерпением. Это может быть или не быть тем, что вы хотите.
type(wat)
или wat.__class__
. Если вы действительно хотите достичь этого, лучше определите класс и сразу создайте его, не нужно испортить декоратор.
– 0xc0de
16 October 2014 в 08:56
wat2 = type(wat)()
, но это питон, мы все соглашаемся с взрослыми и все такое. Вы не можете гарантировать i>, что будет только один экземпляр, но вы можете гарантировать, что если люди сделают второй, он будет выглядеть уродливым и - если они приличные, стоящие люди - как предупреждение знак им. Что мне не хватает?
– Jonas Kölker
2 January 2015 в 20:35
Это немного похоже на ответ fab, но не совсем то же самое.
Контракт singleton не требует, чтобы мы могли вызвать конструктор несколько раз. Поскольку одноэлемент должен создаваться один раз и один раз, разве это не должно быть создано только один раз? «Spoofing» конструктор, возможно, ухудшает удобочитаемость.
Итак, мое предложение таково:
class Elvis():
def __init__(self):
if hasattr(self.__class__, 'instance'):
raise Exception()
self.__class__.instance = self
# initialisation code...
@staticmethod
def the():
if hasattr(Elvis, 'instance'):
return Elvis.instance
return Elvis()
Это не исключает использования конструктора или поля instance
код пользователя:
if Elvis() is King.instance:
... если вы точно знаете, что Elvis
еще не создано и что King
имеет.
Но он рекомендует пользователям использовать универсальный метод the
:
Elvis.the().leave(Building.the())
Чтобы сделать это, вы также можете переопределить __delattr__()
, чтобы поднять исключение, если попытка удалить instance
, и переопределить __del__()
, чтобы он вызывал исключение (если мы не знаем, что программа заканчивается ...)
Спасибо тем, кто помог с комментариями и изменениями, из которых больше приветствуются. Хотя я использую Jython, это должно работать в более общем плане и быть потокобезопасным.
try:
# This is jython-specific
from synchronize import make_synchronized
except ImportError:
# This should work across different python implementations
def make_synchronized(func):
import threading
func.__lock__ = threading.Lock()
def synced_func(*args, **kws):
with func.__lock__:
return func(*args, **kws)
return synced_func
class Elvis(object): # NB must be subclass of object to use __new__
instance = None
@classmethod
@make_synchronized
def __new__(cls, *args, **kwargs):
if cls.instance is not None:
raise Exception()
cls.instance = object.__new__(cls, *args, **kwargs)
return cls.instance
def __init__(self):
pass
# initialisation code...
@classmethod
@make_synchronized
def the(cls):
if cls.instance is not None:
return cls.instance
return cls()
Оповещения:
__new__
__new__
вы должны украсить с помощью @classmethod, или __new__
будет методом несвязанного экземпляра the
класса, возможно, переименовать его в instance
__new
__, а не __init__
, поскольку он чисто действует на атрибуты класса, и это мешает вкратце являясь вторым экземпляром. Разница между этим и методом 2 заключается в том, пытается ли попытка инициализации более одного раза возвращать один экземпляр или вызывает исключение. Я думаю, что я счастлив, что либо удовлетворяю одноэлементному шаблону, но и проще в использовании, тогда как другой более явный, что он является одноэлементным.
– theheadofabroom
21 February 2016 в 10:40
__init__
предотвращает подкласс, но в то время как это упрощает задачу, это не требуется
– theheadofabroom
21 February 2016 в 10:43
__init__
так, что, надеюсь, это должно быть подклассы ...
– mike rodent
21 February 2016 в 10:51
the
, вероятно, может быть полезен для класса класса по аналогичным причинам
– theheadofabroom
21 February 2016 в 11:32
Вот моя собственная реализация синглетонов. Все, что вам нужно сделать, это украсить класс; для получения синглтона вы должны использовать метод Instance
. Вот пример:
@Singleton
class Foo:
def __init__(self):
print 'Foo created'
f = Foo() # Error, this isn't how you get the instance of a singleton
f = Foo.Instance() # Good. Being explicit is in line with the Python Zen
g = Foo.Instance() # Returns already created instance
print f is g # True
И вот код:
class Singleton:
"""
A non-thread-safe helper class to ease implementing singletons.
This should be used as a decorator -- not a metaclass -- to the
class that should be a singleton.
The decorated class can define one `__init__` function that
takes only the `self` argument. Other than that, there are
no restrictions that apply to the decorated class.
To get the singleton instance, use the `Instance` method. Trying
to use `__call__` will result in a `TypeError` being raised.
Limitations: The decorated class cannot be inherited from.
"""
def __init__(self, decorated):
self._decorated = decorated
def Instance(self):
"""
Returns the singleton instance. Upon its first call, it creates a
new instance of the decorated class and calls its `__init__` method.
On all subsequent calls, the already created instance is returned.
"""
try:
return self._instance
except AttributeError:
self._instance = self._decorated()
return self._instance
def __call__(self):
raise TypeError('Singletons must be accessed through `Instance()`.')
def __instancecheck__(self, inst):
return isinstance(inst, self._decorated)
SingletonList = Singleton(list).Instance(); print(SingletonList is type(SingletonList)())
должен печатать True
в истинном одиночном режиме; с вашими отпечатками кода False
– GingerPlusPlus
30 December 2014 в 23:07
Метод 3 кажется очень аккуратным, но если вы хотите, чтобы ваша программа выполнялась как в Python & nbsp; 2 , так и в Python & nbsp; 3 , она не работает. Даже защита отдельных вариантов с помощью тестов для версии Python не выполняется, потому что версия Python & nbsp; 3 дает синтаксическую ошибку в Python & nbsp; 2.
class MyClass(metaclass=Singleton)
– theheadofabroom
30 July 2013 в 15:32
Один вкладыш (я не горжусь, но он выполняет эту работу):
class Myclass:
def __init__(self):
# do your stuff
globals()[type(self).__name__] = lambda: self # singletonify
Этот ответ скорее всего не то, что вы ищете. Мне нужен синглтон в том смысле, что только этот объект имел свою идентичность для сравнения. В моем случае это использовалось как Sentinel Value . Для чего ответ очень прост, сделайте любой объект mything = object()
и природой питона, только эта вещь будет иметь свою идентичность.
#!python
MyNone = object() # The singleton
for item in my_list:
if item is MyNone: # An Example identity comparison
raise StopIteration
Как насчет этого:
def singleton(cls):
instance=cls()
cls.__new__ = cls.__call__= lambda cls: instance
cls.__init__ = lambda self: None
return instance
Используйте его как декоратор в классе, который должен быть одноэлементным. Например:
@singleton
class MySingleton:
#....
Это похоже на декоратор singleton = lambda c: c()
в другом ответе. Как и в другом решении, единственный экземпляр имеет имя класса (MySingleton
). Однако с помощью этого решения вы все равно можете «создать» экземпляры (фактически получить единственный экземпляр) из класса, выполнив MySingleton()
. Это также мешает вам создавать дополнительные экземпляры, выполняя type(MySingleton)()
(который также возвращает тот же экземпляр).
type(MySingleton)()
, вызывается MySingleton.__init__()
, и объект получает инициализацию несколько раз; вы можете исправить это, записав cls.__init__ = lambda self: pass
в свой singleton
. Кроме того, переопределение cls.__call__
кажется бессмысленным и даже вредным - __call__
, определенным в этом контексте, используется, когда вы вызываете MySingleton(any, list, of, arguments)
, а не когда вы вызываете type(MySingleton)(any, list, of, arguments)
.
– GingerPlusPlus
31 December 2014 в 14:23
__init__()
снова вызывается при выполнении type(MySingleton)()
. Предлагаемое вами решение (добавление cls.__init__ = lambda self: pass
) дает синтаксическую ошибку, потому что последняя часть выражения лямбда должна быть выражением, а не выражением. Однако добавление cls.__init__ = lambda self: None
работает, поэтому я добавил это к моему ответу.
– Tolli
2 January 2015 в 06:42
__call__
. мое намерение состояло в том, чтобы вернуть оба экземпляра type(MySingleton)()
и MySingleton()
. Так он делает то, что я хотел. Вы можете думать о том, что MySingleton является либо типом синглтона, либо экземпляром синглтона (или обоих).
– Tolli
2 January 2015 в 06:43
Используйте модуль. Он импортируется только один раз. Определите в нем глобальные переменные - они будут атрибутами singleton. Добавьте некоторые функции - методы «singleton».
__new__
в метаклассе - это когда новый класс class i> является новым - когда он определен, а не когда instance i> будет новым. Вызов класса (MyClass()
) - это операция, которую вы хотите переопределить, а не определение класса. Если вы действительно хотите понять, как работает Python, самое лучшее, что вы можете сделать (кроме использования его), читается docs.python.org/reference/datamodel.html . Хорошей ссылкой на метаклассы является eli.thegreenplace.net/2011/08/14/python-metaclasses-by-example . Хорошая статья о синглонах - это серия из блога google, которую я связал в этом ответе. – agf 20 September 2011 в 07:28