如何在Python中实现虚方法?

Mel*_*oun 69 python virtual-functions

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

它们如何在Python中实现?

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

Eli*_*sky 93

当然,您甚至不必在基类中定义方法.在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()
Run Code Online (Sandbox Code Playgroud)

Cat并且Dog在Python中甚至不必从公共基类派生来允许这种行为 - 你可以免费获得它.也就是说,一些程序员喜欢以更严格的方式定义他们的类层次结构,以便更好地记录它并强加一些严格的键入.这也是可能的 - 例如参见abc标准模块.

  • 例如+1.用什么语言狗会说"hau"呢? (23认同)
  • @Eli:对不起,但我对这个问题的答案非常感兴趣.在英语中他们说"woof",他们不会,但是我们使用的词类似于猫的"喵"和奶牛的"moo".那是"hau"西班牙语吗? (4认同)
  • @JeremyP我肯定知道那是波兰狗说的;) (4认同)
  • @JeremyP:嗯,好点:-)我想在语言中,"h"被理解为使声音像"嬉皮士"的第一个字母或西班牙语中的"Javier". (3认同)
  • @JeremyP 是的,我确认狗用西班牙语说“Jau”,用英语写时会说“Hau”:) hth (2认同)

Ign*_*ams 49

Python方法总是虚拟的.

  • 这个答案并没有真正帮助实现接口类的目标,这是使用虚拟方法的主要原因之一。 (3认同)

Cir*_*四事件 46

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()
Run Code Online (Sandbox Code Playgroud)

得到:

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
Run Code Online (Sandbox Code Playgroud)

相关:是否可以在Python中创建抽象类?

  • 这个答案应该是公认的答案! (5认同)

小智 19

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

from abc import ABCMeta
from abc import abstractmethod
...
class C:
    __metaclass__ = ABCMeta
    @abstractmethod
    def my_abstract_method(self, ...):
Run Code Online (Sandbox Code Playgroud)

如果类不从已经使用元类的类继承,它工作得很好.

来源:http://docs.python.org/2/library/abc.html


小智 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
Run Code Online (Sandbox Code Playgroud)

结果应该是:

I am an unknown animal
I am a dog named Rover
Rover has 4 legs
Run Code Online (Sandbox Code Playgroud)


Kon*_*tin 5

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

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

嗯,几乎总是...

正如 dplamp 指出的那样,并非 Python 中的所有方法都具有这种行为。邓德方法不行。而且我认为这是一个不太知名的功能。

考虑这个人为的例子

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
Run Code Online (Sandbox Code Playgroud)

现在

>>> B().prop_b()
20
>>> A().prob_b()
10
Run Code Online (Sandbox Code Playgroud)

然而,考虑这个

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
Run Code Online (Sandbox Code Playgroud)

现在

>>> B().prop_b()
10
>>> A().prob_b()
10
Run Code Online (Sandbox Code Playgroud)

我们唯一改变的是制作prop_a()一个dunder方法。

第一个行为的问题可能是您无法prop_a()在不影响prop_b(). Raymond Hettinger 的这个非常好的演讲给出了一个不方便的用例示例。