Мне было интересно, есть ли более элегантный и чистый способ кодирования Singleton
декоратора ниже. Я пытался кодировать его как метакласс на основе type
и добавлять metaclass=Singleton
к классам Uid
и Gid
, но это невозможно для сложных объектов из-за этого сообщения об ошибке:
TypeError: конфликт метаклассов: метакласс производного класса должен быть (нестрогим) подклассом метаклассов всех его баз.
from pwd import getpwuid
from grp import getgrgid
from collections import UserString
from weakref import WeakValueDictionary
class NaiveSingleton:
# We have to use WeakValueDictionary so objects can be garbage-collected
instances = WeakValueDictionary()
def __init__(self, cls):
self.cls = cls
def __call__(self, *args, **kwargs):
key = (args, frozenset(kwargs.items()))
if key not in self.instances:
instance = self.cls(*args, **kwargs)
self.instances[key] = instance
return self.instances[key]
class Singleton: # pylint: disable=no-member
"""
Singleton decorator to avoid having multiple objects handling the same args
"""
def __new__(cls, klass):
# We must use WeakValueDictionary() to let the instances be garbage-collected
_dict = dict(cls.__dict__, **{'cls': klass, 'instances': WeakValueDictionary()})
singleton = type(klass.__name__, cls.__bases__, _dict)
return super().__new__(singleton)
def __instancecheck__(self, other):
return isinstance(other, self.cls)
def __call__(self, *args, **kwargs):
key = (args, frozenset(kwargs.items()))
if key not in self.instances:
instance = self.cls(*args, **kwargs)
self.instances[key] = instance
return self.instances[key]
@Singleton
class Uid(UserString, str): # str is added at the end so issubclass(obj, str) works
_name = None
@property
def name(self):
if self._name is None:
self._name = getpwuid(int(self.data)).pw_name
return self._name
@Singleton
class Gid(UserString, str): # str is added at the end so issubclass(obj, str) works
_name = None
@property
def name(self):
if self._name is None:
self._name = getgrgid(int(self.data)).gr_name
return self._name
uid = Uid('0')
gid = Gid('0')
# The id's would be the same if we use the NaiveSingleton
print(id(uid), id(gid))
print(uid.name, gid.name)