1. ホーム
  2. python

[解決済み] 抽象的な属性(プロパティではない)?

2023-01-10 04:50:13

質問

抽象的なインスタンス属性を定義し、プロパティとしては定義しない場合のベストプラクティスは何でしょうか?

というようなことを書きたいと思います。

class AbstractFoo(metaclass=ABCMeta):

    @property
    @abstractmethod
    def bar(self):
        pass

class Foo(AbstractFoo):

    def __init__(self):
        self.bar = 3

の代わりに

class Foo(AbstractFoo):

    def __init__(self):
        self._bar = 3

    @property
    def bar(self):
        return self._bar

    @bar.setter
    def setbar(self, bar):
        self._bar = bar

    @bar.deleter
    def delbar(self):
        del self._bar

プロパティは便利ですが、計算を必要としない単純な属性のためには、過剰なものです。これは、ユーザーがサブクラス化して実装するような抽象クラスでは特に重要です。 @property と書けばいいものを self.foo = foo の中に __init__ ).

Pythonにおける抽象属性 質問は、使用する唯一の答えとして提案する @property@abstractmethod : では私の質問に答えていません。

抽象クラス属性のActiveStateレシピを経由して AbstractAttribute が正しい方法かもしれませんが、私はよくわかりません。また、クラス属性でのみ動作し、インスタンス属性では動作しません。

どのように解決するのですか?

どうしてもサブクラスが所定の属性を定義していることを強制したい場合は、メタクラスを利用します。

 class AbstractFooMeta(type):
 
     def __call__(cls, *args, **kwargs):
         """Called when you call Foo(*args, **kwargs) """
         obj = type.__call__(cls, *args, **kwargs)
         obj.check_bar()
         return obj
     
     
 class AbstractFoo(object):
     __metaclass__ = AbstractFooMeta
     bar = None
 
     def check_bar(self):
         if self.bar is None:
             raise NotImplementedError('Subclasses must define bar')
 
 
 class GoodFoo(AbstractFoo):
     def __init__(self):
         self.bar = 3
 
 
 class BadFoo(AbstractFoo):
     def __init__(self):
         pass

基本的にはメタクラスの再定義 __call__ を再定義して  check_bar がインスタンスの init の後に呼ばれるようにします。

GoodFoo()  # ok
BadFoo ()  # yield NotImplementedError