如何在Python中实现虚拟方法?

How to implement virtual methods in Python?

我知道PHP或Java的虚拟方法。

它们如何在Python中实现?

或者我必须在抽象类中定义一个空方法并重写它吗?


当然,您甚至不必在基类中定义方法。在python中,方法比虚拟方法更好——它们是完全动态的,因为在python中输入的是duck类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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中的CatDog甚至不需要从一个公共的基类派生来允许这种行为—您可以免费获得它。也就是说,一些程序员更喜欢用更严格的方式定义类层次结构,以便更好地记录它,并强制执行一些严格的类型。这也是可能的-例如,参见abc标准模块。


python方法总是虚拟的。


raise NotImplementedError()

这是建议对不实现方法的"抽象"基类的"纯虚拟方法"引发的异常。

https://docs.python.org/3.5/library/exceptions.html notimplementederror说:

This exception is derived from RuntimeError. In user defined base classes, abstract methods should raise this exception when they require derived classes to override the method.

正如其他人所说,这主要是一种文档约定,不需要,但这样您得到的异常比缺少属性错误更有意义。

例如。:

1
2
3
4
5
6
7
8
9
10
11
12
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()

给予:

1
2
3
4
5
6
7
8
9
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提供了一种称为抽象基类的东西,您可以显式地设置如下虚拟方法:

1
2
3
4
5
6
7
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


Python methods are always virtual

就像伊格纳西奥说的不知何故,类继承可能是实现所需内容的更好方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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

结果应该是:

1
2
3
I am an unknown animal
I am a dog named Rover
Rover has 4 legs

Python methods are always virtual.

…前提是他们不是私人的!对一个C++的家伙来说太糟糕了。


类似于C++中的虚拟方法(通过引用或指向基类的指针调用派生类的方法)在Python中没有意义,因为Python没有键入。(我不知道虚拟方法是如何在Java和PHP中工作的。)

但是,如果您所说的"虚拟"是指调用继承层次结构中最底层的实现,那么这就是您在Python中得到的,正如一些答案所指出的那样。

嗯,几乎总是……

正如dplamp所指出的,并非所有的Python方法都是这样。邓德方法没有。我认为这是一个不太知名的特性。

考虑这个人为的例子

1
2
3
4
5
6
7
8
9
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

现在

1
2
3
4
>>> B().prop_b()
20
>>> A().prob_b()
10

但是,考虑一下这个

1
2
3
4
5
6
7
8
9
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

现在

1
2
3
4
>>> B().prop_b()
10
>>> A().prob_b()
10

我们唯一改变的是使prop_a()成为一种邓德方法。

第一个行为的一个问题是,在不影响prop_b()的行为的情况下,您不能更改派生类中prop_a()的行为。RaymondHettinger的这篇非常好的演讲给出了一个不方便使用的用例的例子。