python - singleton應用 - uml singleton




在Python中創建一個單例 (13)

使用Metaclass

我會推薦方法2 ,但最好使用元類而不是基類。 這是一個示例實現:

class Singleton(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

class Logger(object):
    __metaclass__ = Singleton

或者在Python3中

class Logger(metaclass=Singleton):
    pass

如果您想在每次調用該類時運行__init__ ,請添加

        else:
            cls._instances[cls].__init__(*args, **kwargs)

Singleton.__call__if語句。

關於元類的幾句話。 元類是一個類的類 ; 也就是說,一個類是它的元類的一個實例 。 您可以使用type(obj)找到Python中對象的元類。 普通的新式類是類型type 。 上面代碼中的Logger的類型為class 'your_module.Singleton' ,就像Logger的(唯一)實例的類型為class 'your_module.Logger' 。 當你使用Logger()調用logger時,Python首先會詢問LoggerSingleton的元類,該怎麼做,從而允許實例創建被預先佔用。 這個過程與Python通過調用__getattr__在通過執行myclass.attribute引用其中一個屬性時要求某個類執行什麼操作相同。

元類本質上決定的定義是什麼意思,以及如何實現該定義。 例如參見http://code.activestate.com/recipes/498149/ ,它使用元類重新創建了Python中的C風格struct 。 線程什麼是Python中的元類的具體用例? 還提供了一些示例,它們通常似乎與聲明性編程有關,特別是在ORM中使用的。

在這種情況下,如果使用Method#2 ,並且子類定義了__new__方法,則每次調用SubClassOfSingleton() 都會執行它 - 因為它負責調用返回存儲實例的方法。 使用元類時,只會在創建唯一實例時調用它。 你想定制調用該類的意思 ,這是由它的類型決定的。

一般來說,使用元類來實現單例是有意義的 。 單例是特殊的,因為它只創建一次 ,而元類是您自定義創建方式。 如果您需要以其他方式定制單例類定義,使用元類可以提供更多的控制

你的單例不需要多重繼承 (因為元類不是基類),但是對於使用多繼承的已創建類的子類 ,需要確保單例類是第一個/最左邊的類,它重新定義了一個元類__call__這不太可能是一個問題。 實例字典不在實例的名稱空間中,因此它不會意外覆蓋它。

你也會聽說單身模式違反了“單一責任原則” - 每個班級應做一件事 。 這樣,如果您需要更換另一個代碼,您不必擔心代碼會搞亂一件事,因為它們是分開的和封裝的。 元類實現通過了這個測試 。 元類負責強制執行模式 ,創建的類和子類不需要知道它們是單例方法#1未通過此測試,正如您在“MyClass本身是一個函數,而不是一個類,因此您無法從中調用類方法”中所記錄的。

Python 2和3兼容版本

編寫在Python2和Python3中都可用的東西需要使用稍微複雜的方案。 由於元類通常是類型type子類,因此可以使用它在運行時動態創建中間基類並將其作為其元類,然後將用作公共Singleton基類的基類。 如下所示,解釋比做更難:

# works in Python 2 & 3
class _Singleton(type):
    """ A metaclass that creates a Singleton base class when called. """
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(_Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

class Singleton(_Singleton('SingletonMeta', (object,), {})): pass

class Logger(Singleton):
    pass

這種方法的一個諷刺方面是它使用子類來實現元類。 一個可能的優點是,與純元類不同, isinstance(inst, Singleton)將返回True

更正

在另一個主題上,您可能已經註意到了這一點,但原始帖子中的基類實現是錯誤的。 _instances需要在類中引用 ,你需要使用super()或者你正在__new__ ,而__new__實際上是一個靜態方法,你必須將類傳遞給類 ,而不是類方法,因為實際的類沒有當它被調用時被創建 。 所有這些事情對於元類實現也是如此。

class Singleton(object):
  _instances = {}
  def __new__(class_, *args, **kwargs):
    if class_ not in class_._instances:
        class_._instances[class_] = super(Singleton, class_).__new__(class_, *args, **kwargs)
    return class_._instances[class_]

class MyClass(Singleton):
  pass

c = MyClass()

裝飾者返回一個類

我原本是在寫評論,但時間太長了,所以我會在這裡添加。 方法#4比其他的裝飾器版本更好,但它比單獨的代碼更多的代碼,並且它不清楚它的作用。

主要的問題來自類是它自己的基類。 首先,將一個類作為一個幾乎完全相同的類的子類並不奇怪,該類只有在其__class__屬性中才具有相同的名稱? 這也意味著你不能用super() 方法定義任何在它們的基類中調用同名方法的方法,因為它們會遞歸。 這意味著你的類不能自定義__new__ ,並且不能從任何需要調用__init__類派生出來。

何時使用單身模式

你的用例是想要使用單例的更好的例子之一。 你在其中一條評論中說:“對我來說,伐木一直是單身人士的天然選擇。” 你是對的

當人們說單身人士不好時,最常見的原因是他們是隱式共享狀態 。 雖然使用全局變量和頂級模塊導入是顯式共享狀態,但是傳遞的其他對象通常是實例化的。 這是一個很好的觀點, 除了兩個例外

第一個,也是在不同地方提到的,是單身人士是恆定的 。 使用全局常量,尤其是枚舉,被廣泛接受,並被認為是理智的,因為無論如何, 任何用戶都不能將它們混淆為任何其他用戶 。 對於一個不變的單身人士來說也是如此。

第二個例外,即少提及的情況是相反的 - 當單例只是一個數據宿 ,而不是數據源(直接或間接)。 這就是為什麼伐木者覺得自己是單身的“自然”使用。 由於各種用戶不會以其他用戶關心的方式更改記錄器 ,因此並沒有真正的共享狀態 。 這抵消了針對單例模式的主要爭論,並且使它們成為合理的選擇,因為它們易於使用

以下是http://googletesting.blogspot.com/2008/08/root-cause-of-singletons.html的引文:

現在,有一種單身可以。 這是一個單身,其中所有可到達的對像都是不可變的。 如果所有對像都是不可變的,那麼Singleton沒有全局狀態,因為所有對像都是常量。 但把這種單身人士變成易變的單身人士是很容易的,這是非常滑的斜坡。 因此,我也反對這些單身人士,不是因為他們不好,而是因為他們很容易變壞。 (作為一個便箋,Java枚舉就是這種單例,只要你沒有把枚舉放進你的枚舉中,你就可以,所以請不要。)

其他類型的Singletons是半可接受的,它們不影響代碼的執行,它們沒有“副作用”。 記錄就是一個很好的例子。 它加載了單例和全局狀態。 這是可以接受的(因為它不會傷害你),因為不管給定的記錄器是否被啟用,你的應用程序都沒有任何不同。 這裡的信息以一種方式流動:從您的應用程序到記錄器。 即使記錄器是全局狀態,因為沒有信息從記錄器流入您的應用程序,記錄器是可以接受的。 如果你想讓你的測試斷言某些東西被記錄下來,你仍然應該注入你的記錄器,但是一般情況下,儘管記錄器處於滿狀態,但它並不是有害的。

這個問題不是用來討論單例設計模式是否合意,是反模式還是用於任何宗教戰爭,而是討論如何在python中最好地實現這種模式。 在這種情況下,我將'最pythonic'定義為它遵循'最不驚訝的原則'

我有多個類將成為單身人士(我的用例是一個記錄器,但這不重要)。 當我可以簡單地繼承或裝飾時,我不希望雜亂地加上幾個帶有gumph的類。

最佳方法:

方法1:裝飾器

def singleton(class_):
    instances = {}
    def getinstance(*args, **kwargs):
        if class_ not in instances:
            instances[class_] = class_(*args, **kwargs)
        return instances[class_]
    return getinstance

@singleton
class MyClass(BaseClass):
    pass

優點

  • 裝飾器的添加方式通常比多重繼承更直觀。

缺點

  • 儘管使用MyClass()創建的對象將是真正的單例對象,但MyClass本身是一個函數,而不是一個類,因此您無法從中調用類方法。 另外對於m = MyClass(); n = MyClass(); o = type(n)(); m = MyClass(); n = MyClass(); o = type(n)(); 那麼m == n && m != o && n != o

方法2:基類

class Singleton(object):
    _instance = None
    def __new__(class_, *args, **kwargs):
        if not isinstance(class_._instance, class_):
            class_._instance = object.__new__(class_, *args, **kwargs)
        return class_._instance

class MyClass(Singleton, BaseClass):
    pass

優點

  • 這是一個真正的階級

缺點

  • 多重繼承 - eugh! 在繼承第二個基類時, __new__可能被覆蓋? 人們必須思考超過必要的。

方法3: metaclass

class Singleton(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

#Python2
class MyClass(BaseClass):
    __metaclass__ = Singleton

#Python3
class MyClass(BaseClass, metaclass=Singleton):
    pass

優點

  • 這是一個真正的階級
  • 自動奇蹟般地覆蓋繼承
  • 為了正確的目的而使用__metaclass__ (並讓我意識到它)

缺點

  • 有沒有?

方法4:修飾器返回一個同名的類

def singleton(class_):
    class class_w(class_):
        _instance = None
        def __new__(class_, *args, **kwargs):
            if class_w._instance is None:
                class_w._instance = super(class_w,
                                    class_).__new__(class_,
                                                    *args,
                                                    **kwargs)
                class_w._instance._sealed = False
            return class_w._instance
        def __init__(self, *args, **kwargs):
            if self._sealed:
                return
            super(class_w, self).__init__(*args, **kwargs)
            self._sealed = True
    class_w.__name__ = class_.__name__
    return class_w

@singleton
class MyClass(BaseClass):
    pass

優點

  • 這是一個真正的階級
  • 自動奇蹟般地覆蓋繼承

缺點

  • 是否沒有創建每個新班級的開銷? 在這裡,我們為每個我們希望創建單例的類創建兩個類。 雖然這對我來說很好,但我擔心這可能不會擴展。 當然有一個爭論,就是它是否太容易擴展這種模式......
  • _sealed屬性的重點是什麼?
  • 無法使用super()在基類上調用相同名稱的方法,因為它們會遞歸。 這意味著你不能自定義__new__ ,也不能為需要調用__init__的類__new__子類。

I'll toss mine into the ring. It's a simple decorator.

from abc import ABC

def singleton(real_cls):

    class SingletonFactory(ABC):

        instance = None

        def __new__(cls, *args, **kwargs):
            if not cls.instance:
                cls.instance = real_cls(*args, **kwargs)
            return cls.instance

    SingletonFactory.register(real_cls)
    return SingletonFactory

# Usage
@singleton
class YourClass:
    ...  # Your normal implementation, no special requirements.

Benefits I think it has over some of the other solutions:

  • It's clear and concise (to my eye ;D).
  • Its action is completely encapsulated. You don't need to change a single thing about the implementation of YourClass . This includes not needing to use a metaclass for your class (note that the metaclass above is on the factory, not the "real" class).
  • It doesn't rely on monkey-patching anything.
  • It's transparent to callers:
    • Callers still simply import YourClass , it looks like a class (because it is), and they use it normally. No need to adapt callers to a factory function.
    • What YourClass() instantiates is still a true instance of the YourClass you implemented, not a proxy of any kind, so no chance of side effects resulting from that.
    • isinstance(instance, YourClass) and similar operations still work as expected (though this bit does require abc so precludes Python <2.6).

One downside does occur to me: classmethods and staticmethods of the real class are not transparently callable via the factory class hiding it. I've used this rarely enough that I've never happen to run into that need, but it would be easily rectified by using a custom metaclass on the factory that implements __getattr__() to delegate all-ish attribute access to the real class.

A related pattern I've actually found more useful (not that I'm saying these kinds of things are required very often at all) is a "Unique" pattern where instantiating the class with the same arguments results in getting back the same instance. Ie a "singleton per arguments". The above adapts to this well and becomes even more concise:

def unique(real_cls):

    class UniqueFactory(ABC):

        @functools.lru_cache(None)  # Handy for 3.2+, but use any memoization decorator you like
        def __new__(cls, *args, **kwargs):
            return real_cls(*args, **kwargs)

    UniqueFactory.register(real_cls)
    return UniqueFactory

All that said, I do agree with the general advice that if you think you need one of these things, you really should probably stop for a moment and ask yourself if you really do. 99% of the time, YAGNI.


This answer is likely not what you're looking for. I wanted a singleton in the sense that only that object had its identity, for comparison to. In my case it was being used as a Sentinel Value . To which the answer is very simple, make any object mything = object() and by python's nature, only that thing will have its identity.

#!python
MyNone = object()  # The singleton

for item in my_list:
    if item is MyNone:  # An Example identity comparison
        raise StopIteration

This is my preferred way of implementing singletons:

class Test(object):
    obj = None

    def __init__(self):
        if Test.obj is not None:
            raise Exception('A Test Singleton instance already exists')
        # Initialization code here

    @classmethod
    def get_instance(cls):
        if cls.obj is None:
            cls.obj = Test()
        return cls.obj

    @classmethod
    def custom_method(cls):
        obj = cls.get_instance()
        # Custom Code here

一個班輪(我不是很自豪,但它做這項工作):

class Myclass:
  def __init__(self):
      # do your stuff
      globals()[type(self).__name__] = lambda: self # singletonify

代碼基於托利的答案 。

#decorator, modyfies new_cls
def _singleton(new_cls):
    instance = new_cls()                                              #2
    def new(cls):
        if isinstance(instance, cls):                                 #4
            return instance
        else:
            raise TypeError("I can only return instance of {}, caller wanted {}".format(new_cls, cls))
    new_cls.__new__  = new                                            #3
    new_cls.__init__ = lambda self: None                              #5
    return new_cls


#decorator, creates new class
def singleton(cls):
    new_cls = type('singleton({})'.format(cls.__name__), (cls,), {} ) #1
    return _singleton(new_cls)


#metaclass
def meta_singleton(name, bases, attrs):
    new_cls = type(name, bases, attrs)                                #1
    return _singleton(new_cls)

說明:

  1. 創建新的類,從給定的cls繼承
    (它不會修改cls ,以防某人想要singleton(list)

  2. 創建實例。 在覆蓋__new__之前,這很容易。

  3. 現在,當我們輕鬆創建實例時,使用剛才定義的方法覆蓋__new__
  4. 該函數僅在調用者期望的instance返回instance ,否則引發TypeError
    The condition is not met when someone attempts to inherit from decorated class.

  5. If __new__() returns an instance of cls , then the new instance's __init__() method will be invoked like __init__(self[, ...]) , where self is the new instance and the remaining arguments are the same as were passed to __new__() .

    instance is already initialized, so function replaces __init__ with function doing nothing.

See it working online


你可能從來不需要Python中的單例。 只需在一個模塊中定義所有的數據和功能,並且你有一個事實上的單身人士。

如果你真的必須有單身課程,那麼我會選擇:

class My_Singleton(object):
    def foo(self):
        pass

my_singleton = My_Singleton()

使用:

from mysingleton import my_singleton
my_singleton.foo()

其中mysingleton.py是My_Singleton定義的文件名。這是因為在第一次導入文件後,Python不會重新執行代碼。


使用一個模塊。 它只進口一次。 定義一些全局變量 - 它們將是單例的“屬性”。 添加一些功能 - 單身人士的“方法”。


方法3看起來很整潔,但如果你希望你的程序在Python 2Python 3 ,它就不起作用。 即使使用針對Python版本的測試來保護單獨的變體也會失敗,因為Python 3版本在Python 2中給出了語法錯誤。

感謝Mike Watkins: http://mikewatkins.ca/2008/11/29/python-2-and-3-metaclasses/ : http://mikewatkins.ca/2008/11/29/python-2-and-3-metaclasses/ 。 如果您希望程序在Python 2和Python 3中都可以使用,您需要執行以下操作:

class Singleton(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

MC = Singleton('MC', (object), {})

class MyClass(MC):
    pass    # Code for the class implementation

我認為在作業中的'對象'需要用'BaseClass'替換,但我沒有嘗試過(我試過的代碼如圖所示)。



這個怎麼樣:

def singleton(cls):
    instance=cls()
    cls.__new__ = cls.__call__= lambda cls: instance
    cls.__init__ = lambda self: None
    return instance

將它用作應該是單例的類的裝飾器。 喜歡這個:

@singleton
class MySingleton:
    #....

這與另一個答案中的singleton = lambda c: c()裝飾器類似。 與其他解決方案一樣,唯一的實例具有類的名稱( MySingleton )。 但是,通過這種解決方案,您仍然可以通過執行MySingleton()從該類“創建”實例(實際上只獲得唯一的實例MySingleton() 。 它還阻止你通過type(MySingleton)()創建額外的實例(它也返回相同的實例)。


這是我自己實現的單身人士。 你所要做的就是裝飾課堂; 要獲得單身人士,則必須使用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. 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.

    Limitations: The decorated class cannot be inherited from.

    """

    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)

class Foo(object):
     pass

some_global_variable = Foo()

模塊只能導入一次,其他所有內容都會過時。 不要使用單例,並儘量不要使用全局變量。





metaclass