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

Кажется, есть много способов определить singletons в Python. Существует ли консенсусное мнение о Stack Overflow?

Ответ 1

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

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

Ответ 2

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

Ответ 3

Вы можете переопределить метод __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"

Ответ 4

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

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

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

Ответ 5

Подход модуля работает хорошо. Если мне абсолютно нужен синглтон, я предпочитаю подход 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

Ответ 6

См. Эту реализацию от PEP318, реализуя шаблон singleton с помощью декоратора:

def singleton(cls):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance

@singleton
class MyClass:
    ...

Ответ 7

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

Имея это в виду, здесь доказательство понятия:

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

Подробнее о __new__ см. модель данных Python.

Пример:

@singleton
class Duck(object):
    pass

if Duck() is Duck():
    print "It works!"
else:
    print "It doesn't work!"

Примечания:

  • Для этого вам нужно использовать классы нового стиля (исходя из object).

  • Синтаксис инициализируется, когда он определен, а не в первый раз, когда он использовался.

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

Ответ 8

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

class foo:
  x = 1

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

Ответ 9

Документация 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")]
        ...

Ответ 10

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

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

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

Ответ 12

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

def singleton(cls):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance

@singleton
class MyClass:
    ...

Ответ 13

Вот пример из Peter Norvig Python IAQ Как сделать шаблон Singleton в Python? (Вы должны использовать функцию поиска вашего браузера чтобы найти этот вопрос, нет прямой ссылки, извините)

Также у Брюса Эккеля есть еще один пример в его книге Мышление в Python (опять же нет прямой ссылки на код)

Ответ 14

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

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()

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

Ответ 16

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

Я назвал его Singleton_group, потому что он группирует аналогичные экземпляры вместе и предотвращает создание объекта того же класса с одинаковыми аргументами:

# Peppelinux 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 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>}

Каждый объект несет одноэлементный кеш... Это может быть зло, но для некоторых он отлично работает:)

Ответ 17

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

Если вы действительно хотите использовать одноэлементный класс, существует разумная реализация, описанная в впервые попавшая в Google для "Singleton" Python, в частности:

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

Это похоже на трюк.

Ответ 18

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

Ответ 19

Мое простое решение, основанное на значении параметров функции по умолчанию.

def getSystemContext(contextObjList=[]):
    if len( contextObjList ) == 0:
        contextObjList.append( Context() )
        pass
    return contextObjList[0]

class Context(object):
    # Anything you want here

Ответ 20

Синглтонский брат

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

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

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

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

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

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

Ответ 21

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))

Ответ 22

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

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)