使用python中的构造函数限制在单例类中创建对象

Posted

技术标签:

【中文标题】使用python中的构造函数限制在单例类中创建对象【英文标题】:Restricting creation of object in a singleton class using constructor in python 【发布时间】:2019-09-15 13:40:50 【问题描述】:

我正在为 python 中的一项要求编写一个单例类。我可以意识到我无法限制该类的对象创建,如果某些客户端在使用单例类方法之前先使用构造函数进行调用(意味着返回类的唯一实例的方法)。

我的意思是什么?让我用一些代码 sn-p 来解释一下。 考虑我有以下示例之一:


import threading
class MyClass:

    __instance = None

    def __init__(self):

        if self.__instance:
            raise ValueError("Already exist")


    @classmethod
    def getInstance(cls):

        lock = threading.Lock()

        with lock:
            if cls.__instance == None:
                cls.__instance = MyClass()
            return cls.__instance

    def printObjInfo(self,obj):
        print(id(obj))


if __name__ == '__main__':

    ob4 = MyClass()
    ob4.printObjInfo(ob4)

    obj1 = MyClass.getInstance()
    obj1.printObjInfo(obj1)

    obj2 = MyClass.getInstance()
    obj2.printObjInfo(obj2)

    # Create a thread
    obj3 = MyClass.getInstance()
    obj3.printObjInfo(obj3)
    t1 = threading.Thread(target=obj3.printObjInfo, args=(obj3))

如果我运行上面的代码 sn-p,我会得到如下结果:

44824952 - Object created by constructor.
44826240 - Object created by getInstance() method.
44826240 - Object created by getInstance() method.
44826240 - Object created by getInstance() method.

需要注意的一点——如果有人在调用 getInstance() 方法后调用构造函数,那么我们可以轻松地限制其他对象的创建。但是如果先调用它,我们将无法控制它。

现在的问题是 - 1) 我不能在 __init__() 中添加任何进一步的条件,而不是任何人调用它或 2) 不能将我的构造函数设为私有 - 我可以吗?

我在这里找到了一些参考资料 - Program to restrict object creation in Python 但不确定,我们如何限制第一个对象的创建本身。有没有更好的方法让你做到这一点?

有什么想法或参考吗?

谢谢。

【问题讨论】:

在 python 中没有什么是真正私有的。所以这取决于用户如何处理它 @Netwave - 是的,看起来就是这样。 【参考方案1】:

您可以改写__new__

class Singleton:

    _instance = None

    def __init__(self, arg):
        self.arg = arg

    def __new__(cls, arg):
        if cls._instance is None:
            cls._instance = object.__new__(cls)
            return cls._instance

        else:
            return cls._instance

print(Singleton(None))
print(Singleton(None))
print(Singleton(None))

输出:

<__main__.Singleton object at 0x1207fa550>
<__main__.Singleton object at 0x1207fa550>
<__main__.Singleton object at 0x1207fa550>

这样做的好处是有一个统一的接口来创建和获取 Singleton 的单个实例:构造函数。

请注意,除非您编写自己的 C 扩展或类似的东西,否则您将永远无法在 Python 中创建真正的单例:

print(object.__new__(Singleton))
print(object.__new__(Singleton))
print(object.__new__(Singleton))

输出:

<__main__.Singleton object at 0x120806ac8>
<__main__.Singleton object at 0x1207d5b38>
<__main__.Singleton object at 0x1207d5198>

【讨论】:

谢谢,@gmds。有趣的代码 sn-p。我会在最后尝试一下。【参考方案2】:

基于 gmds 的答案的线程安全单例版本

from multiprocessing.dummy import Pool as ThreadPool 
import threading
import time

class ThreadSafeSingleton:
    __instance = None
    __lock = threading.Lock()

    def __init__(self, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs

    def __str__(self):
        return 'id: , args: , kwargs: '.format(id(self), self.args, self.kwargs)

    def __new__(cls, *args, **kwargs):
        with cls.__lock:
            if cls.__instance is None:
                # sleep just simulate heavy class initialization  !!
                # process under concurrency circumstance          !!
                time.sleep(1)
                print('created')
                cls.__instance = super(ThreadSafeSingleton, cls).__new__(cls)
            return cls.__instance


class ThreadUnsafeSingleton:
    __instance = None

    def __init__(self, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs

    def __str__(self):
        return 'id: , args: , kwargs: '.format(id(self), self.args, self.kwargs)

    def __new__(cls, *args, **kwargs):
        if cls.__instance is None:
            time.sleep(1)
            print('created')
            cls.__instance = super(ThreadUnsafeSingleton, cls).__new__(cls)
        return cls.__instance


def create_safe(*args, **kwargs):
    obj = ThreadSafeSingleton(*args, **kwargs)
    print(obj)


def create_unsafe(*args, **kwargs):
    obj = ThreadUnsafeSingleton(*args, **kwargs)
    print(obj)


if __name__ == '__main__':
    pool = ThreadPool(4)
    print('---- test thread safe singleton  ----')
    pool.map(create_safe, range(10))

    print('\n---- test thread unsafe singleton  ----')
    pool.map(create_unsafe, range(10))

输出:

---- test thread safe singleton  ----
created
id: 4473136352, args: (0,), kwargs: 
id: 4473136352, args: (1,), kwargs: 
id: 4473136352, args: (2,), kwargs: 
id: 4473136352, args: (4,), kwargs: 
id: 4473136352, args: (5,), kwargs: 
id: 4473136352, args: (3,), kwargs: 
id: 4473136352, args: (6,), kwargs: 
id: 4473136352, args: (7,), kwargs: 
id: 4473136352, args: (8,), kwargs: 
id: 4473136352, args: (9,), kwargs: 

---- test thread unsafe singleton  ----
created
id: 4473136968, args: (0,), kwargs: 
created
created
created
id: 4473136968, args: (4,), kwargs: 
id: 4473137024, args: (2,), kwargs: 
id: 4473137080, args: (1,), kwargs: 
id: 4473137136, args: (3,), kwargs: 
id: 4473137136, args: (5,), kwargs: 
id: 4473137136, args: (7,), kwargs: 
id: 4473137136, args: (6,), kwargs: 
id: 4473137136, args: (8,), kwargs: 
id: 4473137136, args: (9,), kwargs: 

【讨论】:

以上是关于使用python中的构造函数限制在单例类中创建对象的主要内容,如果未能解决你的问题,请参考以下文章

C++ 难以在单例类中创建类的实例

在单例类构造函数中进行 Ajax 调用

单例模式

如何快速创建单例类? [复制]

单例设计模式

单例设计模式