Pythonでクラスやメソッドを抽象的にするにはどうすればいいですか?
私は__new__()
を次のように再定義しようとしました:
class F:
def __new__(cls):
raise Exception("Unable to create an instance of abstract class %s" %cls)
しかし、G
を継承したクラスF
を作成したとします。
class G(F):
pass
スーパークラスの__new__
メソッドを呼び出すのでG
もインスタンス化できません。
抽象クラスを定義するためのより良い方法はありますか?
抽象クラスを作成するには、 abc
モジュールを使用してください。メソッドabstractを宣言するには abstractmethod
デコレータを使用し、Pythonのバージョンに応じて3つの方法のいずれかを使用してクラスabstractを宣言します。
Python 3.4以降では、 ABC
から継承できます。以前のバージョンのPythonでは、クラスのメタクラスを ABCMeta
として指定する必要があります。メタクラスの指定は、Python 3とPython 2では構文が異なります。3つの可能性が以下に示されています。
# Python 3.4+
from abc import ABC, abstractmethod
class Abstract(ABC):
@abstractmethod
def foo(self):
pass
# Python 3.0+
from abc import ABCMeta, abstractmethod
class Abstract(metaclass=ABCMeta):
@abstractmethod
def foo(self):
pass
# Python 2
from abc import ABCMeta, abstractmethod
class Abstract:
__metaclass__ = ABCMeta
@abstractmethod
def foo(self):
pass
どちらの方法を使用しても、抽象メソッドを持つ抽象クラスをインスタンス化することはできませんが、それらのメソッドの具体的な定義を提供するサブクラスをインスタンス化することはできます。
>>> Abstract()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Abstract with abstract methods foo
>>> class StillAbstract(Abstract):
... pass
...
>>> StillAbstract()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class StillAbstract with abstract methods foo
>>> class Concrete(Abstract):
... def foo(self):
... print('Hello, World')
...
>>> Concrete()
<__main__.Concrete object at 0x7fc935d28898>
これを行うための昔ながらの(pre - PEP 3119 )方法は、abstractメソッドが呼び出されたときにabstractクラスのraise NotImplementedError
することです。
class Abstract(object):
def foo(self):
raise NotImplementedError('subclasses must override foo()!')
class Derived(Abstract):
def foo(self):
print 'Hooray!'
>>> d = Derived()
>>> d.foo()
Hooray!
>>> a = Abstract()
>>> a.foo()
Traceback (most recent call last): [...]
これはabc
モジュールを使うのと同じNiceプロパティを持ちません。抽象基本クラス自体をインスタンス化することはできますが、実行時に抽象メソッドを呼び出すまでは間違いを見つけることはできません。
しかし、少数の抽象メソッドを使用して少数の単純なクラスを処理している場合、このアプローチはabc
のドキュメントを読み進めるよりも少し簡単です。
これは、ABCモジュールを扱わなくても非常に簡単な方法です。
抽象クラスにしたいクラスの__init__
メソッドで、selfの "type"を確認できます。 selfの型が基本クラスの場合、呼び出し元は基本クラスをインスタンス化しようとしているので、例外を発生させます。これは簡単な例です:
class Base():
def __init__(self):
if type(self) is Base:
raise Exception('Base is an abstract class and cannot be instantiated directly')
# Any initialization code
print('In the __init__ method of the Base class')
class Sub(Base):
def __init__(self):
print('In the __init__ method of the Sub class before calling __init__ of the Base class')
super().__init__()
print('In the __init__ method of the Sub class after calling __init__ of the Base class')
subObj = Sub()
baseObj = Base()
実行すると、次のようになります。
In the `__init__` method of the Sub class before calling `__init__` of the Base class
In the `__init__` method of the Base class
In the `__init__` method of the Sub class after calling `__init__` of the Base class
Traceback (most recent call last):
File "/Users/irvkalb/Desktop/Demo files/Abstract.py", line 16, in <module>
baseObj = Base()
File "/Users/irvkalb/Desktop/Demo files/Abstract.py", line 4, in `__init__`
raise Exception('Base is an abstract class and cannot be instantiated directly')
Exception: Base is an abstract class and cannot be instantiated directly
これは、基本クラスから継承したサブクラスをインスタンス化できることを示していますが、基本クラスを直接インスタンス化することはできません。
アーバ
これはpython 3で動作するでしょう
from abc import ABCMeta, abstractmethod
class Abstract(metaclass=ABCMeta):
@abstractmethod
def foo(self):
pass
Abstract()
>>> TypeError: Can not instantiate abstract class Abstract with abstract methods foo
これまでの答えの大部分は正解でしたが、Python 3.7。の答えと例です。はい、抽象クラスとメソッドを作成できます。念のため、クラスは論理的にクラスに属するメソッドを定義する必要がありますが、そのクラスはそのメソッドの実装方法を指定できません。たとえば、以下の「親」と「赤ちゃん」のクラスでは両方とも食事を取りますが、赤ちゃんと親は異なる種類の食べ物を食べるため、また食べる回数も異なるため、実装はそれぞれ異なります。したがって、eatメソッドのサブクラスはAbstractClass.eatをオーバーライドします。
from abc import ABC, abstractmethod
class AbstractClass(ABC):
def __init__(self, value):
self.value = value
super().__init__()
@abstractmethod
def eat(self):
pass
class Parents(AbstractClass):
def eat(self):
return "eat solid food "+ str(self.value) + " times each day"
class Babies(AbstractClass):
def eat(self):
return "Milk only "+ str(self.value) + " times or more each day"
food = 3
mom = Parents(food)
print("moms ----------")
print(mom.eat())
infant = Babies(food)
print("infants ----------")
print(infant.eat())
出力:
moms ----------
eat solid food 3 times each day
infants ----------
Milk only 3 times or more each day
他の回答で説明したように、はい abc
module を使用してPythonで抽象クラスを使用できます。以下に、抽象 @classmethod
、 @property
および @abstractmethod
を使用した実際の例を示します(Python 3.6+を使用)。私にとっては、通常、簡単にコピーして貼り付けることができる例から始める方が簡単です。この答えが他の人にも役立つことを願っています。
最初にBase
という基本クラスを作成しましょう:
from abc import ABC, abstractmethod
class Base(ABC):
@classmethod
@abstractmethod
def from_dict(cls, d):
pass
@property
@abstractmethod
def prop1(self):
pass
@property
@abstractmethod
def prop2(self):
pass
@prop2.setter
@abstractmethod
def prop2(self, val):
pass
@abstractmethod
def do_stuff(self):
pass
Base
クラスには、常にfrom_dict
classmethod
、property
prop1
(読み取り専用)、property
prop2
(設定も可能)、およびdo_stuff
という関数があります。 Base
に基づいて現在構築されているクラスはすべて、メソッド/プロパティにこれらすべてを実装する必要があります。抽象classmethod
および抽象property
の場合、2つのデコレーターが必要であることに注意してください。
これで、次のようなクラスA
を作成できます。
class A(Base):
def __init__(self, name, val1, val2):
self.name = name
self.__val1 = val1
self._val2 = val2
@classmethod
def from_dict(cls, d):
name = d['name']
val1 = d['val1']
val2 = d['val2']
return cls(name, val1, val2)
@property
def prop1(self):
return self.__val1
@property
def prop2(self):
return self._val2
@prop2.setter
def prop2(self, value):
self._val2 = value
def do_stuff(self):
print('juhu!')
def i_am_not_abstract(self):
print('I can be customized')
必要なすべてのメソッド/プロパティが実装されており、もちろん、Base
(ここではi_am_not_abstract
)の一部ではない追加の関数を追加することもできます。
これでできること:
a1 = A('dummy', 10, 'stuff')
a2 = A.from_dict({'name': 'from_d', 'val1': 20, 'val2': 'stuff'})
a1.prop1
# prints 10
a1.prop2
# prints 'stuff'
必要に応じて、prop1
を設定できません。
a.prop1 = 100
戻ります
AttributeError:属性を設定できません
また、from_dict
メソッドは正常に機能します。
a2.prop1
# prints 20
次のように2番目のクラスB
を定義した場合:
class B(Base):
def __init__(self, name):
self.name = name
@property
def prop1(self):
return self.name
そして、このようなオブジェクトをインスタンス化しようとしました:
b = B('iwillfail')
エラーが発生します
TypeError:抽象メソッドdo_stuff、from_dict、prop2で抽象クラスBをインスタンス化できません
Base
で実装しなかったB
で定義されているすべてのものをリストします。
はい、あなたはabc(abstract base classes)モジュールを使ってpythonで抽象クラスを作成することができます。
このサイトはそれを手助けするでしょう: http://docs.python.org/2/library/abc.html
__new__メソッドを利用することもできます。あなたはただ何かを忘れていました。 __new__メソッドは常に新しいオブジェクトを返すので、そのスーパークラスのnewメソッドを返さなければなりません。次のようにしてください。
class F:
def __new__(cls):
if cls is F:
raise TypeError("Cannot create an instance of abstract class '{}'".format(cls.__name__))
return super().__new__(cls)
新しいメソッドを使用するときは、Noneキーワードではなくオブジェクトを返す必要があります。あなたが逃したのはこれだけです。
これも機能し、簡単です。
class A_abstract(object):
def __init__(self):
# quite simple, old-school way.
if self.__class__.__== "A_abstract":
raise NotImplementedError("You can't instantiate this abstract class. Derive it, please.")
class B(A_abstract):
pass
b = B()
# here an exception is raised:
a = A_abstract()