web-dev-qa-db-ja.com

Pythonで仮想メソッドを実装する方法は?

PHPまたはJavaの仮想メソッドを知っています。

Pythonでどのように実装できますか?

または、抽象クラスで空のメソッドを定義してオーバーライドする必要がありますか?

67
Meloun

もちろん、基本クラスでメソッドを定義する必要さえありません。 Pythonのメソッドは仮想よりも優れています-Pythonでの入力はducktypingであるため、メソッドは完全に動的です。

class Dog:
  def say(self):
    print "hau"

class Cat:
  def say(self):
    print "meow"

pet = Dog()
pet.say() # prints "hau"
another_pet = Cat()
another_pet.say() # prints "meow"

my_pets = [pet, another_pet]
for a_pet in my_pets:
  a_pet.say()

PythonのCatおよびDogは、この動作を許可するために共通の基本クラスから派生する必要さえありません-無料で取得できます。ただし、一部のプログラマーは、クラス階層をより厳密な方法で定義してより適切に文書化し、some入力の厳格さを課すことを好みます。これも可能です。たとえば、 abc標準モジュール を参照してください。

92
Eli Bendersky

Pythonメソッドは常に仮想です。

raise NotImplementedError()

これは、メソッドを実装しない「抽象」基本クラスの「純粋仮想メソッド」で発生する推奨例外です。

https://docs.python.org/3.5/library/exceptions.html#NotImplementedError 言います:

この例外は、RuntimeErrorから派生しています。ユーザー定義の基本クラスでは、派生クラスがメソッドをオーバーライドする必要がある場合、抽象メソッドはこの例外を発生させる必要があります。

他の人が言ったように、これはほとんどドキュメントの慣例であり、必須ではありませんが、この方法では、属性が見つからないというエラーよりも意味のある例外が発生します。

例えば。:

class Base(object):
    def virtualMethod(self):
        raise NotImplementedError()
    def usesVirtualMethod(self):
        return self.virtualMethod() + 1

class Derived(Base):
    def virtualMethod(self):
        return 1

print Derived().usesVirtualMethod()
Base().usesVirtualMethod()

与える:

2
Traceback (most recent call last):
  File "./a.py", line 13, in <module>
    Base().usesVirtualMethod()
  File "./a.py", line 6, in usesVirtualMethod
    return self.virtualMethod() + 1
  File "./a.py", line 4, in virtualMethod
    raise NotImplementedError()
NotImplementedError

関連: Pythonで抽象クラスを作成することは可能ですか?

実際、バージョン2.6では、pythonはabstract base classesと呼ばれるものを提供し、次のような仮想メソッドを明示的に設定できます。

from abc import ABCMeta
from abc import abstractmethod
...
class C:
    __metaclass__ = ABCMeta
    @abstractmethod
    def my_abstract_method(self, ...):

クラスが既にメタクラスを使用しているクラスから継承しない限り、非常にうまく機能します。

ソース: http://docs.python.org/2/library/abc.html

19
user2795020

Pythonメソッドは常に仮想です

イグナシオが言ったように、どういうわけかクラス継承はあなたが望むものを実装するためのより良いアプローチかもしれません。

class Animal:
    def __init__(self,name,legs):
        self.name = name
        self.legs = legs

    def getLegs(self):
        return "{0} has {1} legs".format(self.name, self.legs)

    def says(self):
        return "I am an unknown animal"

class Dog(Animal): # <Dog inherits from Animal here (all methods as well)

    def says(self): # <Called instead of Animal says method
        return "I am a dog named {0}".format(self.name)

    def somethingOnlyADogCanDo(self):
        return "be loyal"

formless = Animal("Animal", 0)
rover = Dog("Rover", 4) #<calls initialization method from animal

print(formless.says()) # <calls animal say method

print(rover.says()) #<calls Dog says method
print(rover.getLegs()) #<calls getLegs method from animal class

結果は次のようになります。

I am an unknown animal
I am a dog named Rover
Rover has 4 legs
8
Jinzo

Pythonメソッドは常に仮想です。

...ただし、プライベートではありません! C++の人には残念です。

5
dplamp

Pythonには入力がありません。 (JavaおよびPHPしかし。)で仮想メソッドがどのように機能するかわかりません。)

しかし、「仮想」によって、継承階層の最下位の実装を呼び出すことを意味する場合、いくつかの答えが指摘するように、それは常にPythonで得られるものです。

まあ、ほとんどいつも...

Dplampが指摘したように、Pythonのすべてのメソッドがそのように振る舞うわけではありません。Dunderメソッドはそうではありません。

この人工的な例を考えてみましょう

_class A:
    def prop_a(self):
        return 1
    def prop_b(self):
        return 10 * self.prop_a()

class B(A):
    def prop_a(self):
        return 2
_

いま

_>>> B().prop_b()
20
>>> A().prob_b()
10
_

ただし、これを検討してください

_class A:
    def __prop_a(self):
        return 1
    def prop_b(self):
        return 10 * self.__prop_a()

class B(A):
    def __prop_a(self):
        return 2
_

いま

_>>> B().prop_b()
10
>>> A().prob_b()
10
_

変更した唯一のことは、prop_a()をdunderメソッドにすることでした。

最初の動作の問題は、prop_a()の動作に影響を与えずに、派生クラスのprop_b()の動作を変更できないことです。 これ レイモンド・ヘッティンガーによる非常に素晴らしい話は、これが不便なユースケースの例を示しています。

2
Konstantin