通用单例的类型提示?
Type hinting for generic singleton?
我有一个基础服务,它提供了所有服务的通用方法。此外,此 BaseService
用作服务注册表:
class BaseService:
instances = {}
@classmethod
def get_instance(cls) -> 'BaseService':
if cls.instances.get(cls) is None:
cls.instances[cls] = cls()
return cls.instances[cls]
class Service1(BaseService):
pass
class Service2(BaseService):
pass
Service1.get_instance()
Service2.get_instance()
Service1.get_instance()
get_instance()
方法正在返回子 class 实例,我觉得当前注释 -> 'BaseService'
不正确。我应该如何正确注释此方法?
正如我在评论中所说,为基数class的class方法执行此操作是
有问题,因为根据定义,该方法将与任何 subclasses 共享。对于单身人士来说尤其如此。
解决方法是为每个子class 提供具有适当return 值注释的相似命名方法。虽然这可以使用 class 装饰器来完成,如我的答案的早期版本所示,但使用 metaclass 似乎是一种更简洁的方法,因此我相应地更新了我的答案:
class BaseServiceMeta(type):
""" Metaclass that properly annotates the return value of the get_instance() method of
any subclasses of the BaseService class.
"""
def __new__(metaclass, classname, bases, classdict):
cls = super(metaclass, metaclass).__new__(metaclass, classname, bases, classdict)
if classname != 'BaseService': # subclass?
# define function with the correct return value annotation
def get_instance() -> classname:
return super(cls, cls).get_instance() # call superclass classmethod
setattr(cls, 'get_instance', get_instance) # override inherited method
return cls
class BaseService(metaclass=BaseServiceMeta): # metaclass added
instances = {}
@classmethod
def get_instance(cls) -> 'BaseService':
if cls.instances.get(cls) is None:
cls.instances[cls] = cls()
return cls.instances[cls]
class Service1(BaseService):
pass
class Service2(BaseService):
pass
# show that the methods have the correct return annotation
print(repr(BaseService.get_instance.__annotations__['return'])) # -> 'BaseService'
print(repr( Service1.get_instance.__annotations__['return'])) # -> 'Service1'
print(repr( Service2.get_instance.__annotations__['return'])) # -> 'Service2'
# call subclass methods to show they return the correct singleton instance of each type
print(Service1.get_instance()) # -> <__main__.Service1 object at 0x004A07D0>
print(Service2.get_instance()) # -> <__main__.Service2 object at 0x004A07F0>
print(Service1.get_instance()) # -> <__main__.Service1 object at 0x004A07D0>
我有一个基础服务,它提供了所有服务的通用方法。此外,此 BaseService
用作服务注册表:
class BaseService:
instances = {}
@classmethod
def get_instance(cls) -> 'BaseService':
if cls.instances.get(cls) is None:
cls.instances[cls] = cls()
return cls.instances[cls]
class Service1(BaseService):
pass
class Service2(BaseService):
pass
Service1.get_instance()
Service2.get_instance()
Service1.get_instance()
get_instance()
方法正在返回子 class 实例,我觉得当前注释 -> 'BaseService'
不正确。我应该如何正确注释此方法?
正如我在评论中所说,为基数class的class方法执行此操作是 有问题,因为根据定义,该方法将与任何 subclasses 共享。对于单身人士来说尤其如此。
解决方法是为每个子class 提供具有适当return 值注释的相似命名方法。虽然这可以使用 class 装饰器来完成,如我的答案的早期版本所示,但使用 metaclass 似乎是一种更简洁的方法,因此我相应地更新了我的答案:
class BaseServiceMeta(type):
""" Metaclass that properly annotates the return value of the get_instance() method of
any subclasses of the BaseService class.
"""
def __new__(metaclass, classname, bases, classdict):
cls = super(metaclass, metaclass).__new__(metaclass, classname, bases, classdict)
if classname != 'BaseService': # subclass?
# define function with the correct return value annotation
def get_instance() -> classname:
return super(cls, cls).get_instance() # call superclass classmethod
setattr(cls, 'get_instance', get_instance) # override inherited method
return cls
class BaseService(metaclass=BaseServiceMeta): # metaclass added
instances = {}
@classmethod
def get_instance(cls) -> 'BaseService':
if cls.instances.get(cls) is None:
cls.instances[cls] = cls()
return cls.instances[cls]
class Service1(BaseService):
pass
class Service2(BaseService):
pass
# show that the methods have the correct return annotation
print(repr(BaseService.get_instance.__annotations__['return'])) # -> 'BaseService'
print(repr( Service1.get_instance.__annotations__['return'])) # -> 'Service1'
print(repr( Service2.get_instance.__annotations__['return'])) # -> 'Service2'
# call subclass methods to show they return the correct singleton instance of each type
print(Service1.get_instance()) # -> <__main__.Service1 object at 0x004A07D0>
print(Service2.get_instance()) # -> <__main__.Service2 object at 0x004A07F0>
print(Service1.get_instance()) # -> <__main__.Service1 object at 0x004A07D0>