【问题标题】:Python Class: Singleton or Not Singleton by Passing a Value?Python 类:通过传递值单例还是非单例?
【发布时间】:2019-10-08 13:48:23
【问题描述】:

我有一个 Python 3 类,它目前是使用 @singleton 装饰器定义的单例,但有时它需要是单例。

问题:在从类中实例化一个对象时,是否可以做类似于传递参数的事情,而这个参数决定了该类是单例还是非单例?

我正在尝试找到一种替代方法来复制类并使其不是单例,但是我们将有大量重复的代码。

Foo.py

def singleton(cls):
    instances={}

    def getinstance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]

    return getinstance

@singleton
Class Foo:
    def hello(self):
        print('hello world!')

FooNotSingleton.py

Class FooNotSingleton:
    def hello(self):
        print('hello world!')

main.py

from Foo import Foo
from FooNotSingleton import FooNotSingleton

foo = Foo()
foo.hello()

bar = FooNotSingleton()
bar.hello()

【问题讨论】:

  • 1. _singleton 未定义。你的意思是getinstance?。 2. 愚蠢的问题:为什么不直接删除 @singleton 装饰器?
  • @sanyash 1. 修正错字,谢谢! 2.我希望同一个类有单例和非单例版本,所以单例版本的类应该有@singleton装饰器,非单例版本不应该。也许我错过了一些非常明显的东西?
  • 您介意接受一个给定的答案吗?

标签: python python-3.x singleton


【解决方案1】:

您可以使用关键字触发器在 singleton 包装器中添加一些额外的处理,以绕过类中 singleton=False 的非单一实例化:

def singleton(cls):
    instances={}

    def getinstance(*args, **kwargs):
        # thanks to sanyash's suggestion, using a default return instead of try/except            
        singleton = kwargs.pop('singleton', True)
        if singleton:
            if cls not in instances:
                instances[cls] = cls(*args, **kwargs)
            return instances[cls]
        else:
            return cls(*args, **kwargs)

    return getinstance

@singleton
class Foo:
    def __init__(self, val):
        self.val = val
    def hello(self):
        print(f'I have value {self.val}')

测试:

s1 = Foo('single')
s2 = Foo('another single')
ns = Foo('not single', singleton=False)
s1.hello()
# I have value single
s2.hello()
# I have value single
ns.hello()
# I have value not single

需要注意的是,您需要保留一个不太可能用于您的任何装饰类的关键字。好处是您只需要创建一次类而无需重复。

【讨论】:

  • singleton = kwargs.pop('singleton', True) 可以替换try: ... except 块。
  • 好点,不知道pop() 也有一个不会触发KeyError 的默认值。很高兴知道!
【解决方案2】:

我相信这个问题可以通过继承轻松解决。 FooNotSingleton 成为具有所有实现细节的基类,Foo 使用 @singleton 装饰器派生自它:

FooNotSingleton.py

class FooNotSingleton:
    def hello(self):
        print('hello world!')

Foo.py

import FooNotSingleton

def singleton(cls):
    instances={}

    def getinstance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]

    return getinstance

@singleton
class Foo(FooNotSingleton.FooNotSingleton):
    pass

ma​​in.py

from Foo import Foo
from FooNotSingleton import FooNotSingleton

print(id(FooNotSingleton()))
print(id(FooNotSingleton()))  # different
print(id(Foo()))
print(id(Foo()))  # same
FooNotSingleton().hello()  # both works
Foo().hello()

【讨论】:

    【解决方案3】:

    您可以根据传递给构造函数的唯一 ID 构建实例键。这样,相同的类相同的 ID 将产生相同的实例。

    def singleton(cls):
        instances={}
        def getinstance(*args, **kwargs):
            key = "{}__{}".format(cls, kwargs.get("id"))
            if key not in instances:
                instances[key] = cls(*args, **kwargs)
            return instances[key]
        return getinstance
    
    @singleton
    class Foo:
        def __init__(self, *args, **kwargs):
            self.x = 0
        def hello(self):
            print('My X is:', self.x)
    
    f1 = Foo()
    f1.x = 5
    f1.hello()
    
    f2 = Foo() # same as f1
    f2.hello()
    
    f3 = Foo(id='abc') # new instance, because of new "id" parameter
    f3.x = 1024
    f3.hello()
    
    f4 = Foo() # same as f1
    f4.hello()
    

    输出:

    My X is: 5
    My X is: 5
    My X is: 1024
    My X is: 5
    

    可选:在将 id 参数传递给类构造函数之前,您可以从 kwargs 中删除它 - 并且您可以将 id 命名为完全不同的名称。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-10-24
      • 1970-01-01
      • 1970-01-01
      • 2020-04-01
      • 1970-01-01
      • 2011-06-11
      • 1970-01-01
      相关资源
      最近更新 更多