programing tip

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

itbloger 2020. 9. 2. 14:58
반응형

싱글 톤을 정의하는 간단하고 우아한 방법이 있습니까? [복제]


이 질문에 이미 답변이 있습니다.

파이썬에서 싱글 톤 을 정의하는 방법은 여러 가지가있는 것 같습니다 . Stack Overflow에 대한 합의 의견이 있습니까?


함수가있는 모듈 (클래스가 아님)이 싱글 톤으로도 잘 작동 할 것이기 때문에 나는 실제로 필요성을 보지 못했습니다. 모든 변수는 모듈에 바인딩되며 어쨌든 반복적으로 인스턴스화 할 수 없습니다.

클래스를 사용하려는 경우 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"

Python에서 싱글 톤을 구현하는 약간 다른 접근 방식은 Alex Martelli (Google 직원 및 Python 천재) 보그 패턴 입니다.

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

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


모듈 접근 방식이 잘 작동합니다. 싱글 톤이 절대적으로 필요하다면 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

데코레이터로 싱글 톤 패턴을 구현하는 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

에 대한 자세한 내용은 Python 데이터 모델 을 참조하세요 __new__.

예:

@singleton
class Duck(object):
    pass

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

노트:

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

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

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


나는 이것에 대해 매우 확신하지 못하지만, 내 프로젝트는 '컨벤션 싱글 톤'(강제 된 싱글 톤이 아님)을 사용합니다. 즉,라는 클래스가 있으면 DataController동일한 모듈에서 정의합니다.

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

전체 6 줄이기 때문에 우아하지 않습니다. 그러나 내 모든 싱글 톤은이 패턴을 사용하며, 적어도 매우 명시 적입니다 (파이썬).


파이썬 문서는 이 정보를 포함하고 있는가 :

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 foo:
  x = 1

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

또한 Google Testing 블로그에는 싱글 톤이 왜 나쁘고 안티 패턴인지에 대해 논의하는 흥미로운 기사가 ​​있습니다.


싱글 톤 데코레이터 (일명 어노테이션)를 만드는 것은 앞으로 클래스를 장식 (주석)하려는 경우 우아한 방법입니다. 그런 다음 클래스 정의 앞에 @singleton을 넣습니다.

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

@singleton
class MyClass:
    ...

다음은 Peter Norvig의 Python IAQ의 예입니다. Python 에서 싱글 톤 패턴을 어떻게 수행합니까? (이 질문을 찾으려면 브라우저의 검색 기능을 사용해야합니다. 직접 링크가 없습니다. 죄송합니다.)

또한 Bruce Eckel은 그의 저서 Thinking in Python 에 또 다른 예가 있습니다 (다시 코드에 대한 직접적인 링크가 없습니다).


클래스 나 인스턴스를 싱글 톤으로 강제 하는 것은 과잉 이라고 생각합니다 . 개인적으로 저는 일반 인스턴스화 가능한 클래스, 반 전용 참조 및 간단한 팩토리 함수를 정의하는 것을 좋아합니다.

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

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


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

싱글 톤 클래스를 정말로 원한다면 "Python 싱글 톤" 에 대한 Google첫 번째 히트에 설명 된 합리적인 구현이 있습니다 . 특히 다음과 같습니다.

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

그것은 속임수를 쓰는 것 같습니다.


The Singleton Pattern implemented with Python courtesy of ActiveState.

It looks like the trick is to put the class that's supposed to only have one instance inside of another class.


OK, singleton could be good or evil, I know. This is my implementation, and I simply extend a classic approach to introduce a cache inside and produce many instances of a different type or, many instances of same type, but with different arguments.

I called it Singleton_group, because it groups similar instances together and prevent that an object of the same class, with same arguments, could be created:

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

Every object carries the singleton cache... This could be evil, but it works great for some :)


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

My simple solution which is based on the default value of function parameters.

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

class Context(object):
    # Anything you want here

Singleton's half brother

I completely agree with staale and I leave here a sample of creating a singleton half brother:

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

a will report now as being of the same class as singleton even if it does not look like it. So singletons using complicated classes end up depending on we don't mess much with them.

Being so, we can have the same effect and use simpler things like a variable or a module. Still, if we want use classes for clarity and because in Python a class is an object, so we already have the object (not and instance, but it will do just like).

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

There we have a nice assertion error if we try to create an instance, and we can store on derivations static members and make changes to them at runtime (I love Python). This object is as good as other about half brothers (you still can create them if you wish), however it will tend to run faster due to simplicity.


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

In cases where you don't want the metaclass-based solution above, and you don't like the simple function decorator-based approach (e.g. because in that case static methods on the singleton class won't work), this compromise works:

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)

참고URL : https://stackoverflow.com/questions/42558/python-and-the-singleton-pattern

반응형