在 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()
我们只需访问 self
的 print_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 是抽象的(“纯虚拟”),无法实例化。如果我们尝试创建一个 ParentPrint
或 any subclass 不提供抽象方法的实现,我们会得到一个错误,both 从 mypy
和 运行 时间以 TypeError
的形式静态地在您尝试实例化对象时(在您之前)引发甚至尝试调用抽象方法):
abstract_print = ParentPrint()
# raises error: Cannot instantiate abstract class "ParentPrint" with abstract attribute "print_hello"
考虑以下代码:
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()
我们只需访问 self
的 print_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 是抽象的(“纯虚拟”),无法实例化。如果我们尝试创建一个 ParentPrint
或 any subclass 不提供抽象方法的实现,我们会得到一个错误,both 从 mypy
和 运行 时间以 TypeError
的形式静态地在您尝试实例化对象时(在您之前)引发甚至尝试调用抽象方法):
abstract_print = ParentPrint()
# raises error: Cannot instantiate abstract class "ParentPrint" with abstract attribute "print_hello"