模仿C#的虚函数实现Python的(纯)虚函数

5

如何在Python中模拟C#中的虚函数和纯虚函数?

目前,我使用以下方案:

class AbstractClass(object):
    '''Abstract class'''
    def __init__(self):
        assert False, 'Class cannot be instantiated'

    def _virtual_init(self):
        print 'Base "constructor"'

    def pure_virtual_function(self):
        assert False, 'Pure virtual function should be derived'

    def virtual_function(self):
        print 'Base: virtual function'

class Derived1(AbstractClass):
    '''All functions derived'''
    def __init__(self):
        print 'Derived1: constructor'
        AbstractClass._virtual_init(self)

    def pure_virtual_function(self):
        print 'Derived1: pure virtual function'

    def virtual_function(self):
        print 'Derived1: virtual function'

class Derived2(AbstractClass):
    '''Pure virtual method missing.'''
    def __init__(self):
        print 'Derived2: constructor'
        AbstractClass._virtual_init(self)

    def virtual_function(self):
        print 'Derived3: virtual function'

class Derived3(AbstractClass):
    '''Virtual method missing (use base class)'''
    def __init__(self):
        print 'Derived3: constructor'
        AbstractClass._virtual_init(self)

    def pure_virtual_function(self):
        print 'Derived3: pure virtual function'


# ab = AbstractClass() # Gives error -> OK

print 'DERIVED 1'    
dv1 = Derived1()
dv1.pure_virtual_function()
dv1.virtual_function()

print 'DERIVED 2'    
dv2 = Derived2()
# dv2.pure_virtual_function() # Gives error: Pure virtual function should be derived -> OK
dv2.virtual_function()

print 'DERIVED 3'    
dv3 = Derived3()
# dv3.pure_virtual_function() # Gives error: Pure virtual function should be derived -> OK
dv3.virtual_function()

我得到的输出是:
DERIVED 1
Derived1: constructor
Base "constructor"
Derived1: pure virtual function
Derived1: virtual function
DERIVED 2
Derived2: constructor
Base "constructor"
Derived3: virtual function
DERIVED 3
Derived3: constructor
Base "constructor"
Derived3: pure virtual function
Base: virtual function

然而我的具体问题是:
  1. 在Python(2.7)中是否有更优雅的方法来定义抽象基类? 上述解决方案只会在运行时(而不是例如pylint)出现错误。 Python中没有'abstract'关键字。
  2. 在Python(2.7)中是否有更优雅的方法来定义纯虚方法? 同样,上述解决方案只会在运行时出现错误。
  3. 在Python(2.7)中是否有更优雅的方法来定义虚方法? 不需要检查,但Python中没有'virtual'关键字。
提前感谢。

1
我对这些设计模式并不是很熟悉...但是阅读这段代码让我想起了abc模块。你可能需要去看一下那里,看看它是否提供了你所需要的功能。 - mgilson
1
你永远不会得到比运行时错误更多的东西。除了语法的有效性之外,没有编译时检查可言。(尽管你可以尝试pylint为您执行此类检查。)更好的方法是忘记所有那些漂亮的设计工具。只是去做吧。定义一些具有兼容接口但没有继承的类,传递实例等,而不要试图让计算机强制执行这些不变量。你似乎在用C#/Java的思维方式来思考Python。这会导致糟糕的Python输出,就像Python思维方式会产生可怕的C#/Java代码一样。 - user395760
你为什么要这样做?每种编程语言都有自己的处理方式。在Python中不需要这样做。 - Lanaru
我认为继承是一种非常好的软件设计方式,但在Python中实现得并不是很好...但正如@mgilson已经注意到的那样,有一个很好的abc模块可以克服部分问题。 - Michel Keijzers
2个回答

5
我不确定你在问虚拟方法时具体是什么意思,因为在Python中所有方法都是虚拟的。而且我也不确定为什么你使用 _virtual_init 而不是 __init__ …仅仅重命名一个方法能给你带来什么好处吗?
更新:啊,我有一个想法。你可能这样做是为了禁止实例化基类,同时保留初始化功能。答案是:强制事情不符合Python风格。只需记录此类为抽象类(并可能将其称为 AbstractSmth)。
说到运行时错误,Python是一种动态语言,所以它就是这样工作的。一个常见的习惯用法是在__init__中引发NotImplementedError来表明你的类是抽象的(同样适用于抽象(纯虚拟)方法)。你还可以查看abc,因为它可以禁止实例化抽象类。但我也强烈建议阅读PEP-3119,以了解抽象基类是什么,以及它们不是什么。

据我所知,引发NotImplementedError足以使pylint理解你的类/方法是抽象的。


我使用_virtual_init而不是__init__,因为我不希望用户直接实例化抽象类。但是abc模块可以解决这个问题。文档记录不如强制执行。我知道Python是一种动态语言,NotImplementedError确实比assert更好。感谢PEP-3119链接。 - Michel Keijzers

0
class AbstractBaseClass(object):
    def __init__(self):
        if self.__class__.__name__ == 'AbstractBaseClass':
            raise NotImplementedError("Can't instantiate AbstractBaseClass")

class DerivedClass(AbstractBaseClass):
    def __init__(self):
        super().__init__()
        # ...

这个例子展示了派生类(具有不同的__class__.__name__成员)可以被实例化,并继承AbstractBaseClass提供的属性。但是如果直接实例化AbstractBaseClass,将会抛出NotImplementedError异常。


1
在你的答案中添加一些注释。 - HDJEMAI

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接