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


88

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

如何在Python中实现它们?

还是我要在抽象类中定义一个空方法并覆盖它?

Answers:


104

当然,您甚至不必在基类中定义方法。在Python中,方法要比虚拟方法更好-它们是完全动态的,因为Python中的输入是鸭子输入

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()

Cat而且Dog在Python中,甚至不必从通用基类派生该行为即可-您可以免费获得它。就是说,一些程序员更喜欢以更严格的方式定义其类层次结构,以更好地对其进行记录并施加一定的键入严格性。这也是可能的,例如参见abc标准模块


32
以+1为例。狗用什么语言说“ hau”?
JeremyP 2011年

4
@JeremyP:嗯,要点:-)我猜在某些语言中,“ h”被理解为听起来像西班牙的“嬉皮”或“哈维尔”的首字母。
伊莱·班德斯基

4
@Eli:对不起,但是我对这个问题的答案很感兴趣。用英语他们会说“ woof”,但是他们不会,但是我们用的词类似于猫的“喵”和牛的“ moo”。那么“ hau”是西班牙语吗?
JeremyP 2011年

9
@JeremyP我确定这就是波兰狗所说的;)
j_kubik

2
@JeremyP是的,我确认狗用西班牙语说“ Jau”,用英语书写时会是“ Hau” :) hth
SkyWalker

67

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中创建抽象类?



21

实际上,在2.6版中,python提供了一种称为抽象基类的东西,您可以像这样显式设置虚拟方法:

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 3等效项?
locke14

9

Python方法始终是虚拟的

就像伊格纳西奥(Ignacio)所说的那样,以某种方式,类继承可能是实现所需内容的更好方法。

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

4

像C ++中的虚方法(通过对基类的引用或指针调用派生类的方法实现)之类的东西在Python中没有意义,因为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()。Raymond Hettinger的这个很好的演讲提供了一个用例的例子,其中用例不方便。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.