python - 싱글 톤을 정의하는 간단하고 우아한 방법이 있습니까?


파이썬에서 싱글 톤 을 정의하는 많은 방법이있는 것처럼 보입니다. 에 대한 의견 일치가 있습니까?



Answers


함수가있는 모듈 (클래스가 아닌)이 싱글 톤 (singleton)으로 잘 작동하기 때문에 필요성을 실제로 볼 수는 없습니다. 모든 변수는 모듈에 바인딩되며 어쨌든 반복적으로 인스턴스화 될 수 없습니다.

클래스를 사용하기를 원한다면 Python에서 개인 클래스 나 개인 생성자를 만들 수있는 방법이 없으므로 API를 사용하는 관행을 통하는 것 이외의 여러 인스턴스화를 보호 할 수는 없습니다. 나는 여전히 모듈에 메서드를 넣고 모듈을 싱글 톤으로 간주한다.




내 독단의 구현은 다음과 같습니다. 수업을 꾸미기 만하면됩니다. 싱글 톤을 얻으려면 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)



다음과 같이 __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"



파이썬에서 싱글 톤을 구현하는 약간 다른 접근법은 Alex Martelli (Google 직원 및 Python 천재)의 borg 패턴 입니다.

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

따라서 모든 인스턴스가 동일한 ID를 갖도록 강요하는 대신 상태를 공유합니다.




모듈 방식이 잘 작동합니다. 만약 내가 싱글 톤을 절대적으로 필요로한다면 메타 클래스 접근법을 선호한다.

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



데코레이터로 싱글 톤 패턴을 구현하여 PEP318 에서이 구현을 참조하십시오.

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

@singleton
class MyClass:
    ...



대답이 말하는 것처럼 가장 관용적 인 방법은 모듈을 사용하는 것 입니다.

이를 염두에두고 여기에 개념 증명이 있습니다.

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

노트:

  1. 새로운 스타일의 클래스 ( object 에서 파생 됨)를 사용해야합니다.

  2. 싱글 톤은 정의 될 때 처음 사용될 때가 아니라 초기화 될 때 초기화됩니다.

  3. 이것은 단지 장난감의 예일뿐입니다. 나는 이것을 프로덕션 코드에서 실제로 사용한 적이 없으며 계획하지도 않았습니다.




한 번 파이썬에서 싱글 톤을 썼는데, 모든 멤버 함수에 classmethod 데코레이터가있는 클래스를 사용했습니다.

class foo:
  x = 1

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



나는 이것에 대해 잘 모르지만, 프로젝트는 'convention singletons'을 사용한다. (예를 들어, DataController라는 클래스가 있다면, 동일한 모듈에서 이것을 정의한다.

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

완전한 6 행이기 때문에 우아하지 않습니다. 하지만 내 모든 싱글 톤은이 패턴을 사용하며 적어도 매우 명확합니다 (파이썬 문자입니다).




싱글 톤 데코레이터 (별명이라고도 함)를 만드는 것은 앞으로 클래스를 장식 (주석 달기)하려는 경우 우아한 방법입니다. 그런 다음 클래스 정의 앞에 @singleton을 넣으면됩니다.

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

@singleton
class MyClass:
    ...






파이썬 문서 는 이것을 다루고 있습니다 :

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")]
        ...






나는 클래스 또는 인스턴스를 싱글 톤이되도록 강제 하는 것은 과잉이라고 생각한다. 개인적으로, 나는 정상적인 인스턴스화 가능한 클래스, 준 개인 참조, 그리고 간단한 팩토리 함수를 정의하고 싶다.

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

그렇게하면 부작용없이 새로운 인스턴스에 대한 테스트를 작성할 수 있으며 전역 명령문을 사용하여 모듈을 뿌릴 필요가 없습니다. 필요한 경우 나중에 변형을 파생시킬 수 있습니다.







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



함수 매개 변수의 기본값을 기반으로하는 내 간단한 솔루션입니다.

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

class Context(object):
    # Anything you want here



비교적 새로운 Python에 익숙하기 때문에 가장 일반적인 관용구가 무엇인지 모르겠지만 가장 간단한 것은 클래스 대신 모듈을 사용하는 것입니다. 클래스의 인스턴스 메소드는 모듈의 함수가되고 모든 데이터는 클래스의 멤버가 아닌 모듈의 변수가됩니다. 나는 이것이 사람들이 싱글 톤을 사용하는 문제 유형을 해결하기위한 파이썬 적 접근이라고 생각한다.

싱글 톤 클래스를 정말로 원한다면 "파이썬 싱글 톤" 에 대한 Google첫 번째 히트에 설명 된 합리적인 구현이있다.

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

그 트릭을 할 것으로 보인다.




좋아요, 싱글 톤은 선악 일 수 있습니다. 이것이 제가 구현 한 것입니다. 단순히 내부에 캐시를 도입하고 다른 유형의 여러 인스턴스 또는 동일한 유형의 많은 인스턴스를 생성하고 다른 인수로 생성하는 고전적인 접근 방식을 확장합니다.

비슷한 인스턴스를 함께 그룹화하고 동일한 인수를 사용하여 같은 클래스의 객체를 만들 수 없으므로 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>}

모든 객체는 싱글 톤 캐시를 가지고 있습니다 ... 이것은 악의가있을 수 있지만 어떤 경우에는 훌륭하게 작동합니다 :)




싱글턴의 이복 형제

나는 staale에 완전히 동의하고 여기서 singleton half brother를 만드는 샘플을 남긴다.

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

a 는 마치 싱글 톤과 같지 않더라도 싱글 톤과 동일한 클래스로보고합니다. 그래서 복잡한 클래스를 사용하는 싱글 톤은 우리가 그들에게 많은 것을 낭비하지 않는 것에 따라 끝납니다.

그렇기 때문에 우리는 변수 나 모듈과 같은 효과를 내고 더 단순한 것을 사용할 수 있습니다. 여전히 명확한 클래스를 사용 하고 파이썬에서 클래스가 객체 이기 때문에 우리는 이미 객체를 가지고 있습니다 (객체가 아니라 인스턴스입니다).

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

인스턴스를 만들려고하면 좋은 어설 션 오류가 발생하고 정적 멤버를 파생 클래스에 저장하고 런타임에 변경 사항을 적용 할 수 있습니다 (저는 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))



위의 메타 클래스 기반 솔루션을 원하지 않고 단순 함수 데코레이터 기반 접근 방식을 선호하지 않는 경우 (예 : 싱글 톤 클래스의 정적 메서드가 작동하지 않기 때문에)이 절충안이 작동합니다.

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)