【问题标题】:how to create singleton class with arguments in python如何在python中创建带有参数的单例类
【发布时间】:2019-01-24 14:00:04
【问题描述】:

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

元类

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__

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

装饰器

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

所有这些都可以正常工作,但是当涉及到启动时(例如在普通课程中使用 __init__),我无法找到正确的实现方法。 我能想到的唯一解决方案是以这种方式使用元类方法:

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

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

【问题讨论】:

  • 那篇文章是我的主要有用教程,但正如你所见,他们没有提及“如何传递参数甚至如何设置不同的方法”,这是我的问题跨度>

标签: python python-3.x


【解决方案1】:

我发现实现 Singleton 的最佳方式是使用元类:

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

【讨论】:

  • 我支持这个。我也认为元类是实现单例的最佳方式。但是,看看元类……我一开始发现它们很混乱。
  • @Thomas 当然,我不能说这个)元类很棘手......但是单例很好地利用了它,并且是理解元类概念的良好开端
  • 没错,Python 中的单例实际上让我理解了元类 :-)
  • 我从单例元类中了解到的,就像在使用“metaclass=singleton”的类中重新实现方法 __call__() 以确保为该类创建一个实例。如果您认为实际的单例类是使用 (metaclass=singleton) 的类,那么这是有道理的,
【解决方案2】:

除了@AndriyIvaneyko 的回答,这里有一个线程安全的元类单例实现:

# 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

希望你觉得它有用!

【讨论】:

  • 你的意思是,这将确保通过将整个类作为关键资源(信号量方法)仔细检查值 _instances 来确保只创建一个实例
猜你喜欢
  • 1970-01-01
  • 2013-03-11
  • 2012-03-21
  • 1970-01-01
  • 2015-08-03
  • 1970-01-01
  • 1970-01-01
  • 2020-04-24
相关资源
最近更新 更多