在Python中创建可以扩展为多个派生类的单例元类



我最近遇到了Python的元类概念,我试图对一些现有的类强制执行Singleton。当类再次实例化时,我想重用先前创建的实例。

这是我的尝试。但是单例条件似乎没有被强制执行。

应该执行Singleton的现有类。我有多个这样的类,比如继承了Vehicle的Bike, Train,所有这些都应该是单例的。

from singleton import Singleton
from vehicle import Vehicle
class Car(Vehicle, object):
__metaclass__ = Singleton
def __init__(self):
print("Constructor: Car is starting")
pass
def print_car(self):
print("Car is running")

Car类继承了Vehicle

from abc import ABC
class Vehicle(ABC):
def __init__(self):
pass

这是我在Singleton的尝试。我使用了一些参考SO帖子。

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]

然而,当我为Car创建多个对象时,我没有看到单例被强制执行。

car = Car()
car.print_car()
car2 = Car()
car2.print_car()
print(car is car2)  # Returns False

我还在学习Python的元类以及它是如何工作的,想知道我在这里错过了什么?

所有这些类(基类和派生类)都在我的控制之下,我可以对它们进行更改。我如何实现强制单例我的子类,如Car,Bike等?

(更新1)使用Python 3版本的元类给了我以下错误

from singleton import Singleton
from vehicle import Vehicle
class Car(Vehicle, object, metaclass=Singleton):
# class Car(Vehicle, object):
# __metaclass__ = Singleton
def __init__(self):
print("Constructor: Car is starting")
def print_car(self):
print("Car is running")

错误:

File "/Users/user/HelloVsCode/hello.py", line 5, in <module>
from car import Car
File "/Users/user/HelloVsCode/car.py", line 7, in <module>
class Car(Vehicle, object, metaclass=Singleton):
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

第一:元类对于单例来说是多余的。

如果你想要一个单例,你可以在类被声明后立即实例化它,并扔掉"class";并且只保留它的实例。

像:

class _Sing1:
...
Sing1 = _Sing1()
# done.

如果你希望单例的行为像一个被调用的类,并且它们将总是产生相同的实例,那么一个注入__call__方法并立即实例化类的装饰器也足够简单,并且可以跨多个类使用:

def singleton(cls):
cls.__call__ = lambda self: self
return cls()
@singleton
class Sing2:
...

这对你来说应该足够了,除非你的单例实例本身需要是可调用的。

除此之外,你找到的使用元类创建单例的方法是有效的——问题是如何指定一个类应该使用它作为元类。

你在你的帖子中使用的语法,在类体内声明一个__metaclass__属性,是Python 2确定使用自定义元类的方法。在Python 3中,我们在声明类时只需在metaclaskwarg中指定元类。

这意味着,这将工作,与上面的元类,使您的"汽车"类作为单例工厂工作:

class Car(Vehicle, object, metaclass=Singleton):
def __init__(self):
print("Constructor: Car is starting")

def print_car(self):
print("Car is running")

如果你想在元类的单例反模式中使用ABCMeta,你的元类必须继承ABCMeta——否则你会有元类冲突:

import abc
class Singleton(abc.ABCMeta):
def __call__(...):
...

感谢@jsbueno的回答。以下是解决我的用例的两个解决方案。

方法1:A decorator

使用这种方法,我可以为每个派生类使用decorator

singleton.py

import functools
# https://github.com/siddheshsathe/handy-decorators/blob/master/src/decorators.py   
def singleton(cls):
previous_instances = {}
@functools.wraps(cls)
def wrapper(*args, **kwargs):
if cls in previous_instances and previous_instances.get(cls, None).get('args') == (args, kwargs):
return previous_instances[cls].get('instance')
else:
previous_instances[cls] = {
'args': (args, kwargs),
'instance': cls(*args, **kwargs)
}
return previous_instances[cls].get('instance')
return wrapper

car.py

from singleton import singleton
from vehicle import Vehicle
@singleton
class Car(Vehicle):
def __init__(self):
print("Constructor: Car is starting")
def print_car(self):
print("Car is running")

vehicle.py

from abc import ABC
class Vehicle(ABC):
def __init__(self, vehicle_type, can_fly: bool = False, enable_timeout: bool = False):
self._vehicle_type = vehicle_type
self._can_fly = can_fly
self._enable_timeout = enable_timeout

方法2:元类

裁判:https://stackoverflow.com/a/33364149/73137

使用这种方法,我可以使我的base class为单例。从而在所有派生类上强制执行。

singleton.py

class Singleton(ABCMeta):
_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]

car.py

from vehicle import Vehicle
class Car(Vehicle):
def __init__(self):
print("Constructor: Car is starting")
def print_car(self):
print("Car is running")

vehicle.py

from abc import ABC
from singleton import Singleton
class Vehicle(ABC, metaclass=Singleton):
def __init__(self, vehicle_type, can_fly: bool = False, enable_timeout: bool = False):
self._vehicle_type = vehicle_type
self._can_fly = can_fly
self._enable_timeout = enable_timeout

通用代码:

test.py

from car import Car
car = Car()
car.print_car()
car2 = Car()
car2.print_car()
print(car is car2)

输出:

Constructor: Car is starting
Car is running
Car is running
True

由于我对Python的metaclasssingleton还不熟悉,请告诉我这两种方法之间是否有任何具体的优缺点。

相关内容

  • 没有找到相关文章

最新更新