如何在python中创建带参数的单例类

how to create singleton class with arguments in python

我正在寻找创建在第一次创建时接受参数的单例类的正确方法。我的研究将我引向三种不同的方式:

Metaclass

1
2
3
4
5
6
7
8
9
class Singleton(type):
    instance = None
    def __call__(cls, *args, **kwargs):
        if cls.instance is None:
            cls.instance = super(Singleton, cls).__call__(*args, **kwargs)
        return cls.instance

class ASingleton(metaclass=Singleton):
    pass

__new__

1
2
3
4
5
6
class Singleton(object):
    instance = None
    def __new__(cls, *args, **kwargs):
        if cls.instance is None:
            cls.instance = super().__new__(cls, *args, **kwargs)
        return cls.instance

Decorator

1
2
3
4
5
6
7
8
9
10
11
def Singleton(myClass):
    instances={}
    def getInstance(*args, **kwargs):
        if myClass not in instances:
            instances[myClass] = myClass(*args, **kwargs)
        return instances[myClass]
    return getInstance

@Singleton
class SingletonTest(object):
    pass

所有这些方法都可以很好地工作,但是当涉及到初始化(比如在普通类中使用uuinit_uuu)时,我无法找到正确的方法来实现它。我唯一能想到的解决方案是用这种方式使用元类方法:

1
2
3
4
5
6
7
8
9
10
11
class Singleton(type):
    instance = None

    def __call__(cls, *args, **kwargs):
        if cls.instance is None:
            cls.instance = super(Singleton, cls).__call__(*args, **kwargs)
        return cls.instance

class ASingleton(metaclass=Singleton):
    def __init__(self,j):
        self.j=j

我不知道这是否是创建接受参数的singleton的正确方法。


我发现实现singleton的最佳方法是使用元类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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]

# Python 2
class MyClass():
    __metaclass__= Singleton

# Python 3
class MyClass(metaclass=Singleton):
     pass


除了@andriyivaneyko的答案,这里还有一个线程安全的元类singleton实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Based on tornado.ioloop.IOLoop.instance() approach.
# See https://github.com/facebook/tornado
# Whole idea for this metaclass is taken from: https://stackoverflow.com/a/6798042/2402281
class ThreadSafeSingleton(type):
    _instances = {}
    _singleton_lock = threading.Lock()

    def __call__(cls, *args, **kwargs):
        # double-checked locking pattern (https://en.wikipedia.org/wiki/Double-checked_locking)
        if cls not in cls._instances:
            with cls._singleton_lock:
                if cls not in cls._instances:
                    cls._instances[cls] = super(ThreadSafeSingleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

class YourImplementation(metaclass=ThreadSafeSingleton):
    def __init__(self, *args, **kwargs):
        pass  # your implementation goes here

希望你觉得它有用!


1
2
3
4
5
6
7
8
9
10
11
12
13
14
class OnlyOne:
    class __OnlyOne:
        def __init__(self, arg):
            self.val = arg
        def __str__(self):
            return repr(self) + self.val
    instance = None
    def __init__(self, arg):
        if not OnlyOne.instance:
            OnlyOne.instance = OnlyOne.__OnlyOne(arg)
        else:
            OnlyOne.instance.val = arg
    def __getattr__(self, name):
        return getattr(self.instance, name)

用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
x = OnlyOne('sausage')
print(x)
y = OnlyOne('eggs')
print(y)
z = OnlyOne('spam')
print(z)
print(x)
print(y)
print(`x`)
print(`y`)
print(`z`)
output = '''
<__main__.__OnlyOne instance at 0076B7AC>sausage
<__main__.__OnlyOne instance at 0076B7AC>eggs
<__main__.__OnlyOne instance at 0076B7AC>spam
<__main__.__OnlyOne instance at 0076B7AC>spam
<__main__.__OnlyOne instance at 0076B7AC>spam
<__main__.OnlyOne instance at 0076C54C>
<__main__.OnlyOne instance at 0076DAAC>
<__main__.OnlyOne instance at 0076AA3C>
'''