2017-03-26 10 views
1

私はすべてのサービスに共通のメソッドを提供する基本サービスを持っています。また、この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()方法は、子クラス・インスタンスを返して、私は現在の注釈-> 'BaseService'がincorectであることを感じます。どのようにしてこのメ​​ソッドに正しく注釈を付けるべきですか?

+0

私はあなたを考えていません'classmethod'のためにこれを行うことができます。なぜなら、クラスクラスのすべての派生クラスとベースクラスによって共有されているからです。単一の正しい戻り値はありません。たとえば、 'Service1.get_instance .__ annotations __ ['return'] = 'Service1'を使ってメソッドの注釈を手動で変更した場合、それはベースクラス内のそのメソッドの' get_instance() 'アノテーションにも影響します。クラス。 – martineau

答えて

2

私がコメントしたように、ベースクラスのclassmethodに対してこれを行うのは、定義上、どのメソッドもどのサブクラスとも共有されるため、 に問題があります。これは、特にシングルトンの場合に当てはまります。

回避策は、各サブクラスに適切な戻り値の注釈を持つ同様の名前のメソッドを与えることです。これはクラスのデコレータで行うことができますが、私の答えは、以前のバージョンに示されるように、メタクラスを使用すると、クリーンなアプローチのように思えるので、私はそれに応じて私の答えを更新しました:

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> 
関連する問題