web-dev-qa-db-ja.com

Python純粋な仮想関数が可能であるか、その価値がありますか?

私は、主にC++プログラマーという別の考え方から来ているのかもしれません。この質問は、OOP in Pythonより具体的には純粋な仮想メソッド)に関係しています。そのため、私はコードを採用しました この質問 Iこの基本的なサンプルを見ています。

_class Animal():
    def speak(self):
        print("...")

class Cat(Animal):
    def speak(self):
        print("meow")

class Dog(Animal):
    def speak(self):
        print("woof")

my_pets = [Dog(), Cat(), Dog()]

for _pet in my_pets:
     _pet.speak()
_

つまり、さまざまな派生クラスに対して、speak関数を呼び出すことがわかります。今私の問題は、アヒルのタイピングがすべて良いことであり、私はそれを理解したと思います。しかし、より厳密なOOPを追求することは間違っているでしょうか?それで、私は Abstract Base Classes と具体的には abstractmethod を見ました。これは、基本クラスのメソッドをsuperで呼び出すことを可能にするだけです。話すことなく派生動物を実装するとエラーがスローされるようにspeak()を純粋にする方法/理由はありますか?

そのような追求についての私の議論は、人々がサブクラス化するつもりのモジュールやフレームワークを書くときに、彼らが関数を実装する必要があるという事実を彼らに自己文書化することになるでしょう。おそらく非常に悪い考えは、基本クラスの「純粋な」関数に例外をスローさせるようなものです。問題は、このエラーが実行時に見つかることです。

_class VirtualException(BaseException):
    def __init__(self, _type, _func):
        BaseException(self)

class Animal():
    def speak(self):
        raise VirtualException()

class Cat(Animal):
    def speak(self):
        print("meow")

class Dog(Animal):
    def speak(self):
        print("woof")

class Wildebeest(Animal):
    def function2(self):
        print("What!")

my_pets = [Dog(), Cat(), Dog(), Wildebeest()]

for _pet in my_pets:
    _pet.speak()
_
17
benzeno

抽象基本クラスはすでにあなたが望むことをします。 abstractmethodは、superを使用してメソッドを呼び出すこととは関係ありません。とにかくそれを行うことができます。代わりに、サブクラスをインスタンス化可能にするには、abstractmethodで装飾されたメソッドをオーバーライドする必要があります。

Python 3:

>>> class Foo(metaclass=abc.ABCMeta):
...     @abc.abstractmethod
...     def foo(self):
...         pass
...
>>> class Bar(Foo):
...     pass
...
>>> class Baz(Bar):
...     def foo(self):
...         return super(Baz, self).foo()
...
>>> Foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Foo with abstract methods foo
>>> Bar()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Bar with abstract methods foo
>>> Baz()
<__main__.Baz object at 0x00000210D702E2B0>

Python 2:

>>> class Foo(object):
...     __metaclass__ = abc.ABCMeta
...     @abc.abstractmethod
...     def foo(self): pass
...
>>> class Bar(Foo): pass
...
>>> class Baz(Bar):
...     def foo(self): return super(Baz, self).foo()
...
>>> Foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Foo with abstract methods foo
>>> Bar()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Bar with abstract methods foo
>>> Baz()
<__main__.Baz object at 0x0000000001EC10B8>

問題は、このエラーが実行時に検出されることです!

まあ、それはPythonです...ほとんどのエラーは実行時に表示されます。

私が知る限り、対処する最も一般的なパターンはPythonが基本的にあなたが説明するものです:基本クラスのspeakメソッドに例外をスローさせるだけです:

class Animal():
    def speak(self):
        raise NotImplementedError('You need to define a speak method!')
8
Marius