在 python 中实现的虚拟函数没有任何初始化

Virtual function being implemented without any initialization in python

考虑以下代码:

class parent_print():
    
    def print_word(self):
        self.print_hello()
        
class child_print(parent_print):
    
    def print_hello(self):
        print('Hello')
        
        
basic_print = child_print()
basic_print.print_word()

这里我假设print_hello()是parentclass中的虚函数。并且 parent (parent_print) 的所有 children 都将实现一个名为 print_hello 的方法。这样所有 children 都可以调用单个函数 print_word 并根据 child 完成对 print_hello 函数的适当绑定的方法实现。

但是在像 C++ 这样的语言中,我们需要在 parent class 中用 virtual 关键字明确说明一个函数是虚函数,并且还要使用函数析构函数符号 ~.

但这怎么可能在 python 中没有提到 python parent class 函数 print_hello() 是一个虚函数。

我假设python使用的概念是虚函数,如果我错了请纠正我并解释这个概念。

与其将其视为“虚函数”的问题,不如将其视为“鸭子打字”的示例更有用。在这个表达式中:

self.print_hello()

我们只需访问 selfprint_hello 属性(无论它是什么),然后调用它。如果它没有这样的属性,则会在 运行 时引发 AttributeError。如果该属性不可调用,则会引发 TypeError。这里的所有都是它的。没有对 self 的类型做出任何假设——我们只是要求它“像鸭子一样嘎嘎叫”,看看它是否可以。

duck typing 的危险在于,很容易不小心要求一些实际上不知道如何嘎嘎的东西来嘎嘎——如果你实例化一个 parent_print 并在其上调用 print_word ,它会失败:

abstract_print = parent_print()
abstract_print.print_word()
# raises AttributeError: 'parent_print' object has no attribute 'print_hello'

Python确实支持静态类型声明和抽象classes的概念,可以帮助你避免错误。例如,如果我们 运行 在您的代码 as-is 上使用静态类型检查器 (mypy),我们将得到一个错误:

test.py:4: error: "parent_print" has no attribute "print_hello"

这是完全正确的——从静态类型的角度来看,调用 print_hello 是无效的,因为我们还没有确定所有 parent_print 个实例都具有这样的属性。

为了解决这个问题,我们可以将 print_hello 声明为抽象方法(为了养成良好的习惯,我还将清理名称以符合标准 Python 约定):

from abc import ABC, abstractmethod

class ParentPrint(ABC):
    
    @abstractmethod
    def print_hello(self) -> None: ...

    def print_word(self) -> None:
        self.print_hello()
        
class ChildPrint(ParentPrint):
    
    def print_hello(self) -> None:
        print('Hello')
        
        
basic_print = ChildPrint()
basic_print.print_word()

现在代码类型检查没有问题。

@abstractmethod 装饰器还表明 class 是抽象的(“纯虚拟”),无法实例化。如果我们尝试创建一个 ParentPrintany subclass 不提供抽象方法的实现,我们会得到一个错误,bothmypy 和 运行 时间以 TypeError 的形式静态地在您尝试实例化对象时(在您之前)引发甚至尝试调用抽象方法):

abstract_print = ParentPrint()
# raises error: Cannot instantiate abstract class "ParentPrint" with abstract attribute "print_hello"