Python Class:单例还是非单例传递值?

Python Class: Singleton or Not Singleton by Passing a Value?

我有一个 Python 3 class 目前是使用 @singleton 装饰器定义的单例,但偶尔它需要 而不是 单身。

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

我正在尝试找到一种替代方法来复制 class 并使它不是单例,但那样我们就会有大量重复的代码。

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()

您可以根据传递给构造函数的唯一 ID 构建实例密钥。这样,相同的 class 相同的 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 参数传递给 class 构造函数之前从 kwargs 中删除它 - 当然您可以命名 id 完全不同的东西。

我相信这个问题可以通过继承轻松解决。 FooNotSingleton 成为具有所有实现细节的基础 class,并且 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

main.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()

您可以在 singleton 包装器中使用关键字触发器添加一些额外的处理,以绕过 class:

中带有 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

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