Кажется, есть много способов определить синглтоны в Python. Есть ли единое мнение по поводу переполнения стека?
«этот вопрос не подходит для нашего формата вопросов и ответов» - я думаю, это не субъективный вопрос, есть ли способ задавать такие вопросы, чтобы он соответствовал формату SO Q&A?
Я не согласен с тем, что это неконструктивно. Можно ли было открыть его повторно, если переместить на programmers.stackexchange.com?
@stackoverflowwww нет, потому что это основано на мнениях и прогах. SE этого не любит.
@ratchetfreak Вопрос, который делает популярным, заключается в том, что люди вроде меня ищут разные способы создания синглтонов на Python. Существуют альтернативы со своими плюсами и минусами, которые могут быть подходящими только в определенных ситуациях. Вопрос можно было бы переформулировать следующим образом: «Какие существуют разные способы создания синглтона в Python? Меня особенно интересует разница между решениями, основанными на классе, и решениями, основанными на экземпляре класса».
@stackoverflowwww, это было бы плохо для программистов - это было бы быстро отклонено и закрыто там, см. В чем проблема «за и против»? Рекомендуемая литература: Что происходит на Programmers.SE? Руководство по переполнению стека
@gnat Понятно. Таким образом, следует либо представить решение для синглтона и попросить обзор, либо спросить более конкретно, например «как я могу создать синглтон в Python с помощью класса?». Я на правильном пути?
@stackoverflowwww может сработать что-то подобное. Еще одна вещь, о которой стоит помнить, программисты обычно ожидают солидные исследовательские усилия. Пропустите это, и вы рискуете, что вопрос останется открытым, но вы проголосовали против. Или закрыли как дурак (насколько я помню, там уже много вопросов в теге одиночка)
@ JonasByström они есть, если ваш язык слишком ограничен для их неявной инициализации. в python очень возможно сдержать обещание, что «всякий раз, когда я хочу получить к нему доступ, он будет инициализирован», например (глупая идея), сделав каждое поле свойством «инициализировать, если не инициализировано». но другие проблемы весят больше, и вместо этого вам следует использовать менеджеры контекста или что-то в этом роде.
@flyingsheep: ты вообще ссылку проверял (ты здоровенный Синглтон!)?
Связанный: Создание синглтона в Python






Поскольку я относительно новичок в Python, я не уверен, какая идиома является наиболее распространенной, но самое простое, что я могу придумать, - это просто использовать модуль вместо класса. То, что было бы методами экземпляра в вашем классе, стало бы просто функциями в модуле, а любые данные просто стали бы переменными в модуле, а не членами класса. Я подозреваю, что это питонический подход к решению той проблемы, для решения которой люди используют синглтоны.
Если вам действительно нужен одноэлементный класс, есть разумная реализация, описанная в первое попадание в Google для «Python singleton», а именно:
class Singleton:
__single = None
def __init__( self ):
if Singleton.__single:
raise Singleton.__single
Singleton.__single = self
Кажется, это помогает.
Не хорошо, просто вызывает исключение вместо возврата экземпляра синглтона
Я действительно не вижу в этом необходимости, поскольку модуль с функциями (а не класс) будет хорошо работать как синглтон. Все его переменные будут привязаны к модулю, который в любом случае не может быть повторно создан.
Если вы действительно хотите использовать класс, в Python нет возможности создавать частные классы или частные конструкторы, поэтому вы не можете защитить себя от множественных экземпляров, кроме как только через соглашение об использовании вашего API. Я бы по-прежнему просто помещал методы в модуль и рассматривал модуль как синглтон.
Не мог ли конструктор просто проверить, был ли уже создан экземпляр, и выдать исключение, если оно было?
Это нормально, если вам не нужно использовать наследование как часть вашего дизайна, и в этом случае большинство приведенных ниже ответов более подходят.
Чтобы получить доступ к "свойствам модуля", см .: stackoverflow.com/questions/6841853/…
Означает ли это, что одноэлементные классы вообще невозможны в python?
Существует использование декоратора одноэлементного класса, который будет служить целям одноэлементного шаблона проектирования. Это задокументировано на странице PEP 318 python.org/dev/peps/pep-0318
Не работает при циклическом импорте
что я буду делать, если хочу, чтобы этот модуль был наследуемым?
Противодействовать наследованию решению от Staale бессмысленно. Возможно наследование от функций в Python, поскольку функции являются классами, как и сами модули. Пример: pythonfiddle.com/function-inheritance
На мой взгляд, это неверно. Одно неудобство в интерфейсах уровня модуля - это управление импортом. Например, Python logging - это интерфейс на уровне модуля. Чтобы обеспечить полную очистку после logging, вы должны вызвать в logging.shutdown(). Это означает, что вы должны импортировать logging в модуль, который вызывает shutdown. Если это был одноэлементный шаблон, можно вызвать выключение экземпляра в любом модуле, которому он передан.
Это не ответ на вопрос. Пожалуйста, оставляйте такой ответ на комментарии.
Это также проблема, когда вы не хотите, чтобы функция перезагружала большой объем данных каждый раз, когда она вызывается.
@Matt Вы можете просто передать модуль logging, а затем вызвать в whatever_i_got_logging_as.shutdown(). Или вы можете пройти logging.shutdown, а затем просто запустить whatever_i_got_shutdown_as().
Необходимость может возникнуть из-за возможности лучшего ведения журнала с печатью имени класса в любом из ваших операторов, что может дать дополнительный контекст для вашего журнала. timestamp [MyClass] Something happened
Попытавшись реализовать синглтон с модулем. Я обнаружил, что это просто не работает. Есть много способов, которыми модуль может быть импортирован дважды, что приведет к его потере состояния.
@Duane: Когда модуль впервые помещается в import, он кэшируется в sys.modules, и если он когда-либо импортируется снова, кешированная копия возвращается вместо кода в нем, выполняемого снова, поэтому текущее состояние не нарушается - поэтому часть то, что вы говорите, ошибочно. Эффекты этого почему модули, как говорят, эффективно одиночные.
@Duane: Я нашел хорошее объяснение того, как модули работают в этом отношении, в разделе под названием Как поделиться глобальными переменными между модулями? в онлайн-документации Python (в ее FAQ по программированию).
@martineau К сожалению, это все верно только в теории. Есть лазейки, позволяющие импортировать модули дважды. Например, если модуль дважды появляется в пути Python в разных пространствах имен, он может быть импортирован дважды, потому что система кэширования модулей не может определить один и тот же файл.
Если бы это было правильное решение, шаблон singleton не существовал бы с самого начала. В таких языках, как Java, вы просто используете статический класс. Но это не так.
Несколько иной подход к реализации синглтона в Python - это узор борга Алекса Мартелли (сотрудник Google и гений Python).
class Borg:
__shared_state = {}
def __init__(self):
self.__dict__ = self.__shared_state
Таким образом, вместо того, чтобы заставлять все экземпляры иметь одинаковую идентичность, они разделяют состояние.
Также известен как моносостояние. Возможно, более злобный, чем синглтон.
Не работает с новыми классами стилей
Может ли кто-нибудь объяснить, почему это не работает с классами нового стиля?
@JamesEmerton: Я только что попробовал Python 2.7.2, отлично работает с новыми классами стилей.
instance1 == instance2 получает False !!. это не синглтон, а просто хороший трюк
@pylover: Вы правы, это не синглтон, что, вероятно, является одной из причин, по которой Алекс Мартелли дал ему другое имя, но его эффекты очень похожи.
На самом деле это не синглтон, но очень полезно! +1!
@hasen Это полезно, если вы хотите иметь собственный класс, который хранит ваши значения конфигурации.
Назовите статическую переменную «шаблоном борга» и вуаля! ты гений питона.
Неужели никто другой не видит проблемы параллелизма в этом шаблоне проектирования? Если вы не добавите или не закроете его в замок, вся структура непостоянна.
@retsigam: Синглтоны (или Borgs) фактически такие же, как глобальные переменные, и требуется дополнительный «материал» для предотвращения одновременного доступа к ним и / или их состоянию, если это возможно - как обычно.
@DmitryGrigoryev Когда вы внесли столько же, сколько и Алекс, в других случаях мы тоже назовем вас гением ;-) Его Nutshell - это настоящая книга (хотя и старая), хотя Fluent Рамальо составляет ей некоторую конкуренцию.
Обнаружен @JLPeyret Призыв к достижению.
Модульный подход работает хорошо. Если мне абсолютно нужен синглтон, я предпочитаю подход Metaclass.
class Singleton(type):
def __init__(cls, name, bases, dict):
super(Singleton, cls).__init__(name, bases, dict)
cls.instance = None
def __call__(cls,*args,**kw):
if cls.instance is None:
cls.instance = super(Singleton, cls).__call__(*args, **kw)
return cls.instance
class MyClass(object):
__metaclass__ = Singleton
Этот шаблон противоречит «принципу единой ответственности» (c2.com/cgi/wiki?SingleResponsibilityPrinciple). См. Пункт (2) в blogs.msdn.com/scottdensmore/archive/2004/05/25/140827.aspx.
@haridsv Я не согласен. Тот факт, что класс представляет собой одноэлементный является, абстрагированный в реализации метакласса - сам класс не знает и не заботится о том, что он одноэлементный, поскольку он не отвечает за соблюдение этого требования, это метакласс. Однако, как вы заметили, приведенный ниже метод явно является нарушением. Метод базового класса находится где-то посередине.
проблема в том, что вы можете создать несколько экземпляров от MyClass до deepcopy. Если вы запустите from copy import deepcopy as dcp ; m1 = MyClass() ; m2 = dcp(m1) ; print m1 is m2, вы получите False. Вы знаете, как это обойтись?
@ dare2be: нельзя ли решить проблему копирования, которую вы упомянули, просто добавив метаклассу метод __deepcopy__() к созданному классу?
@Acuminate: не должен ли метакласс переопределять метод __new__() вместо __init__(), чтобы он фактически каждый раз возвращал один и тот же объект?
@martineau: это type.__init__, а не MyClass.__init__.
что, если MyClass наследуется от другого класса? Как я могу избежать «конфликта метаклассов: метакласс производного класса должен быть (нестрогим) подклассом метаклассов всех его баз»
В другом комментарии stackoverflow упоминалось, что вы можете исправить эту ошибку, переопределив новый __ () `` класс SingletonMeta (тип): def _new __ (cls, name, base, dict): dict ['_deepcopy '] = dict [' копировать '] = lambda self, * args: self return super (SingletonMeta, cls) .__ new __ (cls, name, base, dict) `` `- stackoverflow.com/a/9887928/748503
Однажды я написал синглтон на Python, я использовал класс, в котором все функции-члены имели декоратор classmethod.
class foo:
x = 1
@classmethod
def increment(cls, y = 1):
cls.x += y
Мне нравится такой подход, но есть небольшая ошибка. По крайней мере, с Python 2.6 вы не можете заставить такие методы, как __len__ или __getitem__, работать как методы классов, поэтому у вас нет такой гибкости для настройки, как с объектом. Поскольку я часто хочу использовать синглтон как набор данных, это немного разочаровывает.
Мне кажется, что это не что иное, как упаковка кучи вещей в пространство имен ... не то чтобы в этом что-то не так, некоторые даже сказали, что считают их отличной идеей (import this) - просто этот подход не более чем прост и кажется ужасно близким к использованию глобальных переменных, что обычно считается плохой инженерной практикой.
@martineau Я полагаю, что использование синглтона ужасно близко к использованию глобальных переменных, независимо от того, как это реализовано.
Синглтоны лучше глобальных переменных по двум причинам: они вообще не загрязняют глобальное пространство имен (или столько же, как ваш ответ), и что они также обеспечивают ленивую оценку, тогда как глобальные переменные обычно этого не делают (и ваш ответ тоже ).
@DanHomerick для __len__, __getitem__ и даже @property, вы можете использовать __metaclass__, установленный для класса, определяющего вышеуказанное. Отличная работа. Я голосую за класс как синглтон, что по дизайну языка, являясь экземпляром его метакласса. Фактически, все методы могут быть определены в метаклассе, и тогда класс будет использоваться как ссылка на синглтон.
Шаблон Singleton, реализованный с помощью Python любезно предоставлено ActiveState.
Похоже, уловка состоит в том, чтобы поместить класс, который должен иметь только один экземпляр, внутри другого класса.
В блоге Google Testing также есть несколько интересных статей, в которых обсуждается, почему синглтоны являются / могут быть плохими и являются анти-паттерном:
Я помещаю ваши ссылки в отдельные строки, чтобы они не были объединены в одну
Я очень не уверен в этом, но в моем проекте используются «синглтоны соглашения» (не принудительные синглтоны), то есть, если у меня есть класс с именем DataController, я определяю его в том же модуле:
_data_controller = None
def GetDataController():
global _data_controller
if _data_controller is None:
_data_controller = DataController()
return _data_controller
Это не изящно, так как это полные шесть строк. Но все мои синглтоны используют этот шаблон, и он, по крайней мере, очень явный (питонический).
+1 В Python все должно быть связано с соглашениями (потому что обычно вы можете обходить принудительные границы). Лично я предпочитаю метод класса и переменную класса для доступа и хранения экземпляра, поэтому вам не нужно использовать global. (Обычно я не рекомендую использовать global, хотя это один из немногих вариантов использования, где это приемлемо.)
DataController должен быть _DataController? В противном случае можно напрямую указать его
Вы можете переопределить метод __new__ следующим образом:
class Singleton(object):
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(Singleton, cls).__new__(
cls, *args, **kwargs)
return cls._instance
if __name__ == '__main__':
s1 = Singleton()
s2 = Singleton()
if (id(s1) == id(s2)):
print "Same"
else:
print "Different"
ВНИМАНИЕ: если __new __ () возвращает экземпляр cls, то метод __init __ () нового экземпляра будет вызываться как __init __ (self [, ...]), где self - это новый экземпляр, а остальные аргументы такие же, как были передается в __new __ (). Если какой-либо подкласс Singleton реализует __init __ (), он будет вызываться несколько раз с одним и тем же self. Вместо этого я использовал фабрику.
здесь лучше использовать метакласс в качестве ответа: stackoverflow.com/a/33201/804147
Это дает следующее предупреждение - singleton.py:9: DeprecationWarning: object.__new__() takes no parameterscls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
@Siddhant: что еще хуже, в Python 3 это предупреждение становится ошибкой. Подробнее см. bugs.python.org/issue1683368 и blog.jaraco.com/2014/05/….
class Singleton(object[,...]):
staticVar1 = None
staticVar2 = None
def __init__(self):
if self.__class__.staticVar1==None :
# create class instance variable for instantiation of class
# assign class instance variable values to class static variables
else:
# assign class static variable values to class instance variables
Это решение легендарное, я рад, что прошел через все здесь
См. Эту реализацию из PEP318, реализующую одноэлементный шаблон с декоратором:
def singleton(cls):
instances = {}
def getinstance():
if cls not in instances:
instances[cls] = cls()
return instances[cls]
return getinstance
@singleton
class MyClass:
...
Проблема с этим декоратором в том, что MyClass больше не является классом, например super () не будет работать, методы классов не будут работать и т.д.: @singleton class MyClass (BaseClass): def __init __ (self): super (MyClass, self) .__ init __ ()
Кажется, что декоратор должен применяться к методу новый, а не к классу, чтобы справиться с проблемой наследования. В этот момент элегантная читабельность декоратора ухудшается. Или декоратору нужно поиграть с классом, который он украшает, чтобы функция новый сделала что-нибудь разумное.
Я думаю, что принуждение класса или экземпляра, который должен быть синглтоном, является излишним. Лично мне нравится определять обычный экземпляр класса, получастную ссылку и простую фабричную функцию.
class NothingSpecial:
pass
_the_one_and_only = None
def TheOneAndOnly():
global _the_one_and_only
if not _the_one_and_only:
_the_one_and_only = NothingSpecial()
return _the_one_and_only
Или, если нет проблем с созданием экземпляра при первом импорте модуля:
class NothingSpecial:
pass
THE_ONE_AND_ONLY = NothingSpecial()
Таким образом, вы можете писать тесты для новых экземпляров без побочных эффектов, и нет необходимости добавлять в модуль глобальные операторы, а при необходимости вы можете получить варианты в будущем.
Я считаю, что это хуже, чем перебор. Принуждение к неявному поведению просто не питонично. И у меня есть еще комментарии к повторяющемуся ответу :-) stackoverflow.com/questions/31875/…
Вот моя собственная реализация синглтонов. Все, что вам нужно сделать, это украсить класс; чтобы получить синглтон, вам необходимо использовать метод 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. Also, the decorated class cannot be
inherited from. 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.
"""
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)
Python включает аккумулятор, это должно быть частью стандартной библиотеки desing_pattern, спасибо
Я новичок в питоне. Если я украслю свой базовый класс @Singleton, станут ли дочерние классы одним из них?
@ EgeAydın Вы не сможете наследовать класс, украшенный @Singleton, поэтому не будет никаких дочерних классов. Это техническое ограничение, а не намеренно.
Этот декоратор не поддерживает конструкторы с аргументами. Для декоратора более общего назначения используйте: def Instance(self, *args, **kwargs): self._instance = self._decorated(*args, **kwargs).
@akhan Я специально решил не поддерживать конструкторы с аргументами, потому что аргументы будут использоваться только в первый раз и игнорировать все остальные. Это может затруднить отслеживание вашего кода, поскольку вы можете использовать разные аргументы в разных местах, но вы можете не знать, какой из этих вызовов фактически инициализирует синглтон.
@akhan Если вы действительно хотите инициализировать синглтон с аргументами, у вас должен быть отдельный метод initialize(), который может принимать любые аргументы и генерировать выбросы, если вызывается более одного раза.
Спасибо! Есть идеи, как заставить pylint не жаловаться на это? (желательно ничего не отключая). Я получаю ошибку Class MyClass has no 'Instance' member.
@DennisGolomazov Вы неправильно написали заглавные буквы.
@PaulManta Кстати, вы можете использовать type(Foo.instance())() для создания нового.
@ wizzwizz4 Ах, хороший момент, но в этот момент вы просто напрашиваетесь на неприятности. Цель состоит в том, чтобы предотвратить создание дополнительных экземпляров по ошибке, а не во избежание его взлома.
@PaulManta NoneType - настоящий синглтон в этом отношении. (Просто указываю, что есть способ сделать это без недостатков, который поддерживается Python; вы правы, что это не проблема.)
Это одноэлементная реализация действительно плохо. Во-первых, это неправильный декоратор, потому что он не использует functools.wraps или functools.update_wrapper. Во-вторых, получение экземпляра путем вызова Foo.Instance() ужасно непифонично, и есть ровно 0 причин, по которым он не мог быть реализован как Foo(). В-третьих, замена такого класса дает неожиданные результаты вроде type(Foo.instance()) is Foo -> False.
Справедливо, я могу согласиться, что что-то вроде Foo.get_instance() более самодокументируется, чем просто Foo(). Но две другие вещи, которые я сказал, все еще верны, и это все еще реализация действительно плохо.
@ Аран-Фей, похоже, это решение действительно лопнет твой пузырь, смеется. Я не верю, что Пол Манта когда-либо говорил, что это лучшее решение в мире. Он просто пытался ответить на первоначальный вопрос авторов. Я считаю, что это отличное решение проблемы «его отсутствия» в Python.
В случаях, когда вам не нужно решение на основе метакласса, описанное выше, и вам не нравится подход на основе простого декоратора функций (например, потому что в этом случае статические методы в одноэлементном классе не будут работать), этот компромисс работает:
class singleton(object):
"""Singleton decorator."""
def __init__(self, cls):
self.__dict__['cls'] = cls
instances = {}
def __call__(self):
if self.cls not in self.instances:
self.instances[self.cls] = self.cls()
return self.instances[self.cls]
def __getattr__(self, attr):
return getattr(self.__dict__['cls'], attr)
def __setattr__(self, attr, value):
return setattr(self.__dict__['cls'], attr, value)
Мое простое решение, основанное на значениях параметров функции по умолчанию.
def getSystemContext(contextObjList=[]):
if len( contextObjList ) == 0:
contextObjList.append( Context() )
pass
return contextObjList[0]
class Context(object):
# Anything you want here
Создание одноэлементного декоратора (также известного как аннотация) - это элегантный способ, если вы хотите украсить (аннотировать) классы в будущем. Затем вы просто помещаете @singleton перед определением вашего класса.
def singleton(cls):
instances = {}
def getinstance():
if cls not in instances:
instances[cls] = cls()
return instances[cls]
return getinstance
@singleton
class MyClass:
...
Интересно, почему за это не проголосовали? Отлично ... объясните, пожалуйста, почему и как вызывается метод getinstance?
Похоже, вы скопировали PEP318?
@YugalJindle: К вашему сведению, вкратце, здесь функция декоратора класса заменяет переданный ей объект класса функцией, которая возвращает либо новый экземпляр класса, который он создает, вызывая его, когда он создается в первый раз, или копию того первого, если это не первый раз.
Одна потенциальная - хотя, вероятно, незначительная - проблема с этим подходом заключается в том, что имя класса в конечном итоге будет привязано к функции, а не к объекту класса. Это означает, что создать подкласс MyClass с помощью обычного оператора class Derived(MyClass) будет невозможно.
Я бы сказал, что проблема, поднятая @martineau, является основной с этим подходом, поскольку весь смысл использования класса, а не модуля, заключается в возможности создавать подклассы.
@tiho: Я не согласен, что это серьезная проблема по нескольким причинам. Некоторые из них: это легко исправить / обойти, по крайней мере, несколькими способами, и я думаю, что основная причина создания классов - это инкапсуляция, а не разрешение или поддержка наследования, что особенно верно в отношении одноэлементных классов.
-1, это решение является дубликатом предыдущего, опубликованного в 08 @ lambda-fairy, поэтому попытка публикации должна была сделать следующий ответ дельтой, если есть что добавить к предыдущему сообщению! Не уверен, что вижу дельту, и я не склонен проверять
Хорошо, я знаю, что синглтон может быть добром или злом. Это моя реализация, и я просто расширяю классический подход, чтобы ввести кеш внутри и создать множество экземпляров другого типа или множество экземпляров одного и того же типа, но с разными аргументами.
Я назвал его Singleton_group, потому что он группирует похожие экземпляры вместе и предотвращает создание объекта того же класса с одинаковыми аргументами:
# Peppelinux's cached singleton
class Singleton_group(object):
__instances_args_dict = {}
def __new__(cls, *args, **kwargs):
if not cls.__instances_args_dict.get((cls.__name__, args, str(kwargs))):
cls.__instances_args_dict[(cls.__name__, args, str(kwargs))] = super(Singleton_group, cls).__new__(cls, *args, **kwargs)
return cls.__instances_args_dict.get((cls.__name__, args, str(kwargs)))
# It's a dummy real world use example:
class test(Singleton_group):
def __init__(self, salute):
self.salute = salute
a = test('bye')
b = test('hi')
c = test('bye')
d = test('hi')
e = test('goodbye')
f = test('goodbye')
id(a)
3070148780L
id(b)
3070148908L
id(c)
3070148780L
b == d
True
b._Singleton_group__instances_args_dict
{('test', ('bye',), '{}'): <__main__.test object at 0xb6fec0ac>,
('test', ('goodbye',), '{}'): <__main__.test object at 0xb6fec32c>,
('test', ('hi',), '{}'): <__main__.test object at 0xb6fec12c>}
Каждый объект несет синглтон-кеш ... Это может быть плохо, но для некоторых это отлично работает :)
Документация Python покрывает это:
class Singleton(object):
def __new__(cls, *args, **kwds):
it = cls.__dict__.get("__it__")
if it is not None:
return it
cls.__it__ = it = object.__new__(cls)
it.init(*args, **kwds)
return it
def init(self, *args, **kwds):
pass
Я бы, наверное, переписал его, чтобы он выглядел примерно так:
class Singleton(object):
"""Use to create a singleton"""
def __new__(cls, *args, **kwds):
"""
>>> s = Singleton()
>>> p = Singleton()
>>> id(s) == id(p)
True
"""
self = "__self__"
if not hasattr(cls, self):
instance = object.__new__(cls)
instance.init(*args, **kwds)
setattr(cls, self, instance)
return getattr(cls, self)
def init(self, *args, **kwds):
pass
Чтобы расширить это, он должен быть относительно чистым:
class Bus(Singleton):
def init(self, label=None, *args, **kwds):
self.label = label
self.channels = [Channel("system"), Channel("app")]
...
+1 за то, что был единственным, кто упомянул реализацию Гвидо ван Россума. Однако ваша собственная версия неверна: вы не должны использовать hasattr и getattr внутри __new__, поскольку они оба вызывают object.__getattribute__, который, в свою очередь, ищет ваш атрибут "__self__" во всей иерархии классов, а не только в текущем классе. Если Guido использует __dict__ для доступа к атрибутам, это не случайно. Попробуйте: class A(GuidoSingleton): pass, class B(A): pass, class C(YourSingleton): pass, class D(C): pass, print(A(), B(), C(), D()). Все подклассы относятся к одному и тому же экземпляру с YourSingleton!
+1 за напоминание о том, что документация Python - это всегда лучшее место для начала поиска синглтонов и других шаблонов проектирования.
class Singeltone(type):
instances = dict()
def __call__(cls, *args, **kwargs):
if cls.__name__ not in Singeltone.instances:
Singeltone.instances[cls.__name__] = type.__call__(cls, *args, **kwargs)
return Singeltone.instances[cls.__name__]
class Test(object):
__metaclass__ = Singeltone
inst0 = Test()
inst1 = Test()
print(id(inst1) == id(inst0))
Как говорит принятый ответ, самый идиоматичный способ - просто использовать модуль.
Имея это в виду, вот доказательство концепции:
def singleton(cls):
obj = cls()
# Always return the same object
cls.__new__ = staticmethod(lambda cls: obj)
# Disable __init__
try:
del cls.__init__
except AttributeError:
pass
return cls
См. Модель данных Python для получения более подробной информации о __new__.
Пример:
@singleton
class Duck(object):
pass
if Duck() is Duck():
print "It works!"
else:
print "It doesn't work!"
Примечания:
Для этого вы должны использовать классы нового стиля (производные от object).
Синглтон инициализируется при его определении, а не при первом использовании.
Это просто игрушечный пример. Я никогда не использовал это в производственном коде и не планирую.
Я пробовал это, но получил ошибку: TypeError: несвязанный метод
Спасибо что подметил это. Понятия не имею, почему это происходит, но отредактированная версия должна работать на Python 2.7 и 3.3.
Это нехорошо, метод __init__() вызывается при определении класса (хотя вы можете подождать до его первого использования), а затем при каждом вызове Duck().
Я задокументировал первую проблему и исправил вторую. Спасибо, что указали на это.
Сводный брат Синглтона
Я полностью согласен с staale и оставляю здесь образец создания единокровного брата-одиночки:
class void:pass
a = void();
a.__class__ = Singleton
a теперь будет сообщать, что он принадлежит к тому же классу, что и синглтон, даже если он на него не похож. Таким образом, синглтоны, использующие сложные классы, оказываются в зависимости от того, что мы с ними особо не возимся.
Таким образом, мы можем добиться того же эффекта и использовать более простые вещи, такие как переменная или модуль. Тем не менее, если мы хотим использовать классы для ясности и потому, что в Python класс - это объект, значит, у нас уже есть объект (не и экземпляр, но он будет работать точно так же).
class Singleton:
def __new__(cls): raise AssertionError # Singletons can't have instances
Здесь у нас есть хорошая ошибка утверждения, если мы пытаемся создать экземпляр, и мы можем хранить в производных статических элементах и вносить в них изменения во время выполнения (я люблю Python). Этот объект ничем не хуже других сводных братьев (вы все равно можете создать их, если хотите), однако он будет работать быстрее из-за простоты.