Есть ли простой и элегантный способ определения синглтонов?

Кажется, есть много способов определить синглтоны в Python. Есть ли единое мнение по поводу переполнения стека?

Синглтоны - патологические лжецы, are they not?
Jonas Byström 05.07.2012 19:01

«этот вопрос не подходит для нашего формата вопросов и ответов» - я думаю, это не субъективный вопрос, есть ли способ задавать такие вопросы, чтобы он соответствовал формату SO Q&A?

binithb 14.09.2014 02:13

Я не согласен с тем, что это неконструктивно. Можно ли было открыть его повторно, если переместить на programmers.stackexchange.com?

gogo_gorilla 06.08.2015 12:35

@stackoverflowwww нет, потому что это основано на мнениях и прогах. SE этого не любит.

ratchet freak 06.08.2015 12:44

@ratchetfreak Вопрос, который делает популярным, заключается в том, что люди вроде меня ищут разные способы создания синглтонов на Python. Существуют альтернативы со своими плюсами и минусами, которые могут быть подходящими только в определенных ситуациях. Вопрос можно было бы переформулировать следующим образом: «Какие существуют разные способы создания синглтона в Python? Меня особенно интересует разница между решениями, основанными на классе, и решениями, основанными на экземпляре класса».

gogo_gorilla 06.08.2015 12:49

@stackoverflowwww, это было бы плохо для программистов - это было бы быстро отклонено и закрыто там, см. В чем проблема «за и против»? Рекомендуемая литература: Что происходит на Programmers.SE? Руководство по переполнению стека

gnat 06.08.2015 17:05

@gnat Понятно. Таким образом, следует либо представить решение для синглтона и попросить обзор, либо спросить более конкретно, например «как я могу создать синглтон в Python с помощью класса?». Я на правильном пути?

gogo_gorilla 06.08.2015 17:16

@stackoverflowwww может сработать что-то подобное. Еще одна вещь, о которой стоит помнить, программисты обычно ожидают солидные исследовательские усилия. Пропустите это, и вы рискуете, что вопрос останется открытым, но вы проголосовали против. Или закрыли как дурак (насколько я помню, там уже много вопросов в теге одиночка)

gnat 06.08.2015 17:46

@ JonasByström они есть, если ваш язык слишком ограничен для их неявной инициализации. в python очень возможно сдержать обещание, что «всякий раз, когда я хочу получить к нему доступ, он будет инициализирован», например (глупая идея), сделав каждое поле свойством «инициализировать, если не инициализировано». но другие проблемы весят больше, и вместо этого вам следует использовать менеджеры контекста или что-то в этом роде.

flying sheep 01.10.2015 12:36

@flyingsheep: ты вообще ссылку проверял (ты здоровенный Синглтон!)?

Jonas Byström 01.10.2015 13:46

Связанный: Создание синглтона в Python

Aran-Fey 28.05.2018 16:07
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
470
11
312 655
21
Перейти к ответу Данный вопрос помечен как решенный

Ответы 21

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

Если вам действительно нужен одноэлементный класс, есть разумная реализация, описанная в первое попадание в Google для «Python singleton», а именно:

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

Кажется, это помогает.

Не хорошо, просто вызывает исключение вместо возврата экземпляра синглтона

pylover 31.01.2013 02:50
Ответ принят как подходящий

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

Если вы действительно хотите использовать класс, в Python нет возможности создавать частные классы или частные конструкторы, поэтому вы не можете защитить себя от множественных экземпляров, кроме как только через соглашение об использовании вашего API. Я бы по-прежнему просто помещал методы в модуль и рассматривал модуль как синглтон.

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

Casebash 16.05.2010 11:56

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

Jim Jeffries 06.06.2011 12:23

Чтобы получить доступ к "свойствам модуля", см .: stackoverflow.com/questions/6841853/…

Frosty Z 27.07.2011 14:22

Означает ли это, что одноэлементные классы вообще невозможны в python?

Yugal Jindle 22.11.2011 09:21

Существует использование декоратора одноэлементного класса, который будет служить целям одноэлементного шаблона проектирования. Это задокументировано на странице PEP 318 python.org/dev/peps/pep-0318

James Hurford 09.02.2012 08:34

Не работает при циклическом импорте

dieend 25.09.2012 14:49

что я буду делать, если хочу, чтобы этот модуль был наследуемым?

yossi 26.11.2012 13:09

Противодействовать наследованию решению от Staale бессмысленно. Возможно наследование от функций в Python, поскольку функции являются классами, как и сами модули. Пример: pythonfiddle.com/function-inheritance

Christophe 11.04.2015 16:38

На мой взгляд, это неверно. Одно неудобство в интерфейсах уровня модуля - это управление импортом. Например, Python logging - это интерфейс на уровне модуля. Чтобы обеспечить полную очистку после logging, вы должны вызвать в logging.shutdown(). Это означает, что вы должны импортировать logging в модуль, который вызывает shutdown. Если это был одноэлементный шаблон, можно вызвать выключение экземпляра в любом модуле, которому он передан.

Matt 25.02.2016 09:49

Это не ответ на вопрос. Пожалуйста, оставляйте такой ответ на комментарии.

Shayne 21.10.2017 11:10

Это также проблема, когда вы не хотите, чтобы функция перезагружала большой объем данных каждый раз, когда она вызывается.

Nickpick 10.11.2017 16:34

@Matt Вы можете просто передать модуль logging, а затем вызвать в whatever_i_got_logging_as.shutdown(). Или вы можете пройти logging.shutdown, а затем просто запустить whatever_i_got_shutdown_as().

wizzwizz4 21.04.2018 21:25

Необходимость может возникнуть из-за возможности лучшего ведения журнала с печатью имени класса в любом из ваших операторов, что может дать дополнительный контекст для вашего журнала. timestamp [MyClass] Something happened

rodi 05.07.2018 22:22

Попытавшись реализовать синглтон с модулем. Я обнаружил, что это просто не работает. Есть много способов, которыми модуль может быть импортирован дважды, что приведет к его потере состояния.

Duane 25.12.2018 03:51

@Duane: Когда модуль впервые помещается в import, он кэшируется в sys.modules, и если он когда-либо импортируется снова, кешированная копия возвращается вместо кода в нем, выполняемого снова, поэтому текущее состояние не нарушается - поэтому часть то, что вы говорите, ошибочно. Эффекты этого почему модули, как говорят, эффективно одиночные.

martineau 05.01.2019 12:33

@Duane: Я нашел хорошее объяснение того, как модули работают в этом отношении, в разделе под названием Как поделиться глобальными переменными между модулями? в онлайн-документации Python (в ее FAQ по программированию).

martineau 05.01.2019 13:30

@martineau К сожалению, это все верно только в теории. Есть лазейки, позволяющие импортировать модули дважды. Например, если модуль дважды появляется в пути Python в разных пространствах имен, он может быть импортирован дважды, потому что система кэширования модулей не может определить один и тот же файл.

Duane 06.01.2019 21:41

Если бы это было правильное решение, шаблон singleton не существовал бы с самого начала. В таких языках, как Java, вы просто используете статический класс. Но это не так.

Noctiphobia 19.01.2020 08:01

Несколько иной подход к реализации синглтона в Python - это узор борга Алекса Мартелли (сотрудник Google и гений Python).

class Borg:
    __shared_state = {}
    def __init__(self):
        self.__dict__ = self.__shared_state

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

Также известен как моносостояние. Возможно, более злобный, чем синглтон.

Tom Hawtin - tackline 01.11.2009 14:52

Не работает с новыми классами стилей

James Emerton 05.06.2010 03:54

Может ли кто-нибудь объяснить, почему это не работает с классами нового стиля?

Stephen Emslie 09.06.2011 13:08

@JamesEmerton: Я только что попробовал Python 2.7.2, отлично работает с новыми классами стилей.

voithos 26.09.2012 21:22

instance1 == instance2 получает False !!. это не синглтон, а просто хороший трюк

pylover 31.01.2013 02:54

@pylover: Вы правы, это не синглтон, что, вероятно, является одной из причин, по которой Алекс Мартелли дал ему другое имя, но его эффекты очень похожи.

martineau 10.04.2013 23:47

На самом деле это не синглтон, но очень полезно! +1!

Moss 31.10.2014 15:32

@hasen Это полезно, если вы хотите иметь собственный класс, который хранит ваши значения конфигурации.

ostrokach 01.02.2016 22:07

Назовите статическую переменную «шаблоном борга» и вуаля! ты гений питона.

Dmitry Grigoryev 19.02.2016 15:49

Неужели никто другой не видит проблемы параллелизма в этом шаблоне проектирования? Если вы не добавите или не закроете его в замок, вся структура непостоянна.

retsigam 10.04.2018 01:39

@retsigam: Синглтоны (или Borgs) фактически такие же, как глобальные переменные, и требуется дополнительный «материал» для предотвращения одновременного доступа к ним и / или их состоянию, если это возможно - как обычно.

martineau 05.01.2019 12:45

@DmitryGrigoryev Когда вы внесли столько же, сколько и Алекс, в других случаях мы тоже назовем вас гением ;-) Его Nutshell - это настоящая книга (хотя и старая), хотя Fluent Рамальо составляет ей некоторую конкуренцию.

JL Peyret 30.04.2020 07:55

Обнаружен @JLPeyret Призыв к достижению.

Dmitry Grigoryev 30.04.2020 16:46

Модульный подход работает хорошо. Если мне абсолютно нужен синглтон, я предпочитаю подход 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 20.05.2010 23:21

@haridsv Я не согласен. Тот факт, что класс представляет собой одноэлементный является, абстрагированный в реализации метакласса - сам класс не знает и не заботится о том, что он одноэлементный, поскольку он не отвечает за соблюдение этого требования, это метакласс. Однако, как вы заметили, приведенный ниже метод явно является нарушением. Метод базового класса находится где-то посередине.

agf 23.07.2011 08:38

проблема в том, что вы можете создать несколько экземпляров от MyClass до deepcopy. Если вы запустите from copy import deepcopy as dcp ; m1 = MyClass() ; m2 = dcp(m1) ; print m1 is m2, вы получите False. Вы знаете, как это обойтись?

user312650 27.03.2012 13:29

@ dare2be: нельзя ли решить проблему копирования, которую вы упомянули, просто добавив метаклассу метод __deepcopy__() к созданному классу?

martineau 03.12.2012 22:18

@Acuminate: не должен ли метакласс переопределять метод __new__() вместо __init__(), чтобы он фактически каждый раз возвращал один и тот же объект?

martineau 03.12.2012 22:27

@martineau: это type.__init__, а не MyClass.__init__.

Eric 03.02.2014 04:00

что, если MyClass наследуется от другого класса? Как я могу избежать «конфликта метаклассов: метакласс производного класса должен быть (нестрогим) подклассом метаклассов всех его баз»

Nickpick 26.10.2017 17:21

В другом комментарии 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

James McGuigan 24.11.2017 21:38

Однажды я написал синглтон на Python, я использовал класс, в котором все функции-члены имели декоратор classmethod.

class foo:
  x = 1

  @classmethod
  def increment(cls, y = 1):
    cls.x += y

Мне нравится такой подход, но есть небольшая ошибка. По крайней мере, с Python 2.6 вы не можете заставить такие методы, как __len__ или __getitem__, работать как методы классов, поэтому у вас нет такой гибкости для настройки, как с объектом. Поскольку я часто хочу использовать синглтон как набор данных, это немного разочаровывает.

Dan Homerick 30.07.2010 03:19

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

martineau 10.12.2012 07:42

@martineau Я полагаю, что использование синглтона ужасно близко к использованию глобальных переменных, независимо от того, как это реализовано.

David Locke 12.12.2012 20:11

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

martineau 13.12.2012 01:30

@DanHomerick для __len__, __getitem__ и даже @property, вы можете использовать __metaclass__, установленный для класса, определяющего вышеуказанное. Отличная работа. Я голосую за класс как синглтон, что по дизайну языка, являясь экземпляром его метакласса. Фактически, все методы могут быть определены в метаклассе, и тогда класс будет использоваться как ссылка на синглтон.

Leonid Usov 05.08.2015 20:47

Шаблон Singleton, реализованный с помощью Python любезно предоставлено ActiveState.

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

В блоге Google Testing также есть несколько интересных статей, в которых обсуждается, почему синглтоны являются / могут быть плохими и являются анти-паттерном:

Я помещаю ваши ссылки в отдельные строки, чтобы они не были объединены в одну

kͩeͣmͮpͥ ͩ 01.11.2011 13:24

Я очень не уверен в этом, но в моем проекте используются «синглтоны соглашения» (не принудительные синглтоны), то есть, если у меня есть класс с именем DataController, я определяю его в том же модуле:

_data_controller = None
def GetDataController():
    global _data_controller
    if _data_controller is None:
        _data_controller = DataController()
    return _data_controller

Это не изящно, так как это полные шесть строк. Но все мои синглтоны используют этот шаблон, и он, по крайней мере, очень явный (питонический).

+1 В Python все должно быть связано с соглашениями (потому что обычно вы можете обходить принудительные границы). Лично я предпочитаю метод класса и переменную класса для доступа и хранения экземпляра, поэтому вам не нужно использовать global. (Обычно я не рекомендую использовать global, хотя это один из немногих вариантов использования, где это приемлемо.)

schlamar 11.01.2013 14:23

DataController должен быть _DataController? В противном случае можно напрямую указать его

nos 13.11.2020 23:19

Вы можете переопределить метод __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. Вместо этого я использовал фабрику.

alsuren 08.09.2011 16:48

здесь лучше использовать метакласс в качестве ответа: stackoverflow.com/a/33201/804147

underrun 23.01.2012 19:24

Это дает следующее предупреждение - singleton.py:9: DeprecationWarning: object.__new__() takes no parameterscls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)

Siddhant 02.01.2013 20:17

@Siddhant: что еще хуже, в Python 3 это предупреждение становится ошибкой. Подробнее см. bugs.python.org/issue1683368 и blog.jaraco.com/2014/05/….

Jason R. Coombs 14.06.2014 20:36

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

Это решение легендарное, я рад, что прошел через все здесь

Weilory 21.09.2020 13:16

См. Эту реализацию из 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 __ ()

mkm 28.10.2011 13:40

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

F1Rumors 29.06.2016 20:01

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

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/…

schlamar 11.01.2013 14:29

Вот моя собственная реализация синглтонов. Все, что вам нужно сделать, это украсить класс; чтобы получить синглтон, вам необходимо использовать метод 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, спасибо

dashesy 31.08.2014 04:23

Я новичок в питоне. Если я украслю свой базовый класс @Singleton, станут ли дочерние классы одним из них?

Ege Aydın 19.03.2017 00:22

@ EgeAydın Вы не сможете наследовать класс, украшенный @Singleton, поэтому не будет никаких дочерних классов. Это техническое ограничение, а не намеренно.

Paul Manta 19.03.2017 01:12

Этот декоратор не поддерживает конструкторы с аргументами. Для декоратора более общего назначения используйте: def Instance(self, *args, **kwargs): self._instance = self._decorated(*args, **kwargs).

akhan 28.04.2017 08:16

@akhan Я специально решил не поддерживать конструкторы с аргументами, потому что аргументы будут использоваться только в первый раз и игнорировать все остальные. Это может затруднить отслеживание вашего кода, поскольку вы можете использовать разные аргументы в разных местах, но вы можете не знать, какой из этих вызовов фактически инициализирует синглтон.

Paul Manta 28.04.2017 10:53

@akhan Если вы действительно хотите инициализировать синглтон с аргументами, у вас должен быть отдельный метод initialize(), который может принимать любые аргументы и генерировать выбросы, если вызывается более одного раза.

Paul Manta 28.04.2017 10:54

Спасибо! Есть идеи, как заставить pylint не жаловаться на это? (желательно ничего не отключая). Я получаю ошибку Class MyClass has no 'Instance' member.

Dennis Golomazov 20.07.2017 00:32

@DennisGolomazov Вы неправильно написали заглавные буквы.

wizzwizz4 21.04.2018 21:27

@PaulManta Кстати, вы можете использовать type(Foo.instance())() для создания нового.

wizzwizz4 21.04.2018 21:28

@ wizzwizz4 Ах, хороший момент, но в этот момент вы просто напрашиваетесь на неприятности. Цель состоит в том, чтобы предотвратить создание дополнительных экземпляров по ошибке, а не во избежание его взлома.

Paul Manta 21.04.2018 23:38

@PaulManta NoneType - настоящий синглтон в этом отношении. (Просто указываю, что есть способ сделать это без недостатков, который поддерживается Python; вы правы, что это не проблема.)

wizzwizz4 21.04.2018 23:40

Это одноэлементная реализация действительно плохо. Во-первых, это неправильный декоратор, потому что он не использует functools.wraps или functools.update_wrapper. Во-вторых, получение экземпляра путем вызова Foo.Instance() ужасно непифонично, и есть ровно 0 причин, по которым он не мог быть реализован как Foo(). В-третьих, замена такого класса дает неожиданные результаты вроде type(Foo.instance()) is Foo -> False.

Aran-Fey 28.05.2018 15:59

Справедливо, я могу согласиться, что что-то вроде Foo.get_instance() более самодокументируется, чем просто Foo(). Но две другие вещи, которые я сказал, все еще верны, и это все еще реализация действительно плохо.

Aran-Fey 25.07.2018 23:35

@ Аран-Фей, похоже, это решение действительно лопнет твой пузырь, смеется. Я не верю, что Пол Манта когда-либо говорил, что это лучшее решение в мире. Он просто пытался ответить на первоначальный вопрос авторов. Я считаю, что это отличное решение проблемы «его отсутствия» в Python.

JokerMartini 05.07.2019 18:17

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

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?

Yugal Jindle 10.03.2012 15:43

Похоже, вы скопировали PEP318?

Yugal Jindle 10.03.2012 15:53

@YugalJindle: К вашему сведению, вкратце, здесь функция декоратора класса заменяет переданный ей объект класса функцией, которая возвращает либо новый экземпляр класса, который он создает, вызывая его, когда он создается в первый раз, или копию того первого, если это не первый раз.

martineau 13.12.2012 00:22

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

martineau 13.12.2012 00:36

Я бы сказал, что проблема, поднятая @martineau, является основной с этим подходом, поскольку весь смысл использования класса, а не модуля, заключается в возможности создавать подклассы.

tiho 10.04.2013 21:42

@tiho: Я не согласен, что это серьезная проблема по нескольким причинам. Некоторые из них: это легко исправить / обойти, по крайней мере, несколькими способами, и я думаю, что основная причина создания классов - это инкапсуляция, а не разрешение или поддержка наследования, что особенно верно в отношении одноэлементных классов.

martineau 11.04.2013 00:06

-1, это решение является дубликатом предыдущего, опубликованного в 08 @ lambda-fairy, поэтому попытка публикации должна была сделать следующий ответ дельтой, если есть что добавить к предыдущему сообщению! Не уверен, что вижу дельту, и я не склонен проверять

msudder 03.07.2016 18:40

Хорошо, я знаю, что синглтон может быть добром или злом. Это моя реализация, и я просто расширяю классический подход, чтобы ввести кеш внутри и создать множество экземпляров другого типа или множество экземпляров одного и того же типа, но с разными аргументами.

Я назвал его 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!

Maggyero 15.12.2017 19:56

+1 за напоминание о том, что документация Python - это всегда лучшее место для начала поиска синглтонов и других шаблонов проектирования.

user-asterix 13.11.2018 03:58

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!"

Примечания:

  1. Для этого вы должны использовать классы нового стиля (производные от object).

  2. Синглтон инициализируется при его определении, а не при первом использовании.

  3. Это просто игрушечный пример. Я никогда не использовал это в производственном коде и не планирую.

Я пробовал это, но получил ошибку: TypeError: несвязанный метод () должен вызываться с экземпляром Integer в качестве первого аргумента (вместо этого получен экземпляр типа) Мой класс Integer - это ваш класс Duck: @singleton class Integer (объект): "" "Класс для объектов целочисленного типа" "" pass

Tom Prats 11.03.2013 11:03

Спасибо что подметил это. Понятия не имею, почему это происходит, но отредактированная версия должна работать на Python 2.7 и 3.3.

Lambda Fairy 12.03.2013 12:02

Это нехорошо, метод __init__() вызывается при определении класса (хотя вы можете подождать до его первого использования), а затем при каждом вызове Duck().

tiho 10.04.2013 21:46

Я задокументировал первую проблему и исправил вторую. Спасибо, что указали на это.

Lambda Fairy 13.04.2013 11:54

Сводный брат Синглтона

Я полностью согласен с staale и оставляю здесь образец создания единокровного брата-одиночки:

class void:pass
a = void();
a.__class__ = Singleton

a теперь будет сообщать, что он принадлежит к тому же классу, что и синглтон, даже если он на него не похож. Таким образом, синглтоны, использующие сложные классы, оказываются в зависимости от того, что мы с ними особо не возимся.

Таким образом, мы можем добиться того же эффекта и использовать более простые вещи, такие как переменная или модуль. Тем не менее, если мы хотим использовать классы для ясности и потому, что в Python класс - это объект, значит, у нас уже есть объект (не и экземпляр, но он будет работать точно так же).

class Singleton:
    def __new__(cls): raise AssertionError # Singletons can't have instances

Здесь у нас есть хорошая ошибка утверждения, если мы пытаемся создать экземпляр, и мы можем хранить в производных статических элементах и ​​вносить в них изменения во время выполнения (я люблю Python). Этот объект ничем не хуже других сводных братьев (вы все равно можете создать их, если хотите), однако он будет работать быстрее из-за простоты.

Другие вопросы по теме