【问题标题】:How to implement virtual methods in Python?如何在 Python 中实现虚方法?
【发布时间】:2011-06-10 11:48:21
【问题描述】:

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

如何在 Python 中实现它们?

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

【问题讨论】:

    标签: python virtual-functions


    【解决方案1】:

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

    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 standard module

    【讨论】:

    • +1 为例。顺便说一句,狗用什么语言说“hau”?
    • @JeremyP: 嗯,好点 :-) 我猜在语言中,“h”被理解为发出类似于“嬉皮士”或西班牙语中“哈维尔”的第一个字母的声音。
    • @Eli:对不起,我对这个问题的答案非常感兴趣。在英语中,他们说“woof”,但他们不会,但这是我们使用的词,类似于猫的“喵”和奶牛的“哞”。那么“hau”是西班牙语吗?
    • @JeremyP 我确信波兰狗会这么说;)
    • @JeremyP 是的,我确认狗用西班牙语说“Jau”,用英文写成“Hau”:) hth
    【解决方案2】:

    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
    

    相关:Is it possible to make abstract classes in Python?

    【讨论】:

      【解决方案3】:

      Python 方法总是虚拟的。

      【讨论】:

      • dunder 方法除外。
      • 这个答案并没有真正帮助实现接口类的目标,这是使用虚拟方法的主要原因之一。
      【解决方案4】:

      实际上,在 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?
      【解决方案5】:

      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
      

      【讨论】:

        【解决方案6】:

        像 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
        

        现在

        >>> 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_b() 的行为的情况下更改派生类中prop_a() 的行为。 This Raymond Hettinger 的非常好的演讲给出了一个不方便的用例示例。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-07-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-06-07
          • 1970-01-01
          • 1970-01-01
          • 2013-03-24
          相关资源
          最近更新 更多