Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mimic Python (pure) virtual functions like C#

What is the best way in Python to mimic virtual and pure virtual functions like in C#?

Currently I use a schema like:

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()

I get as output:

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

However my specific questions are:

  1. Is there a more elegant way to define an abstract base class in Python (2.7)? The above solution only gives an error at run time (not e.g. with pylint) There is no 'abstract' keyword in Python afaik.
  2. Is there a more elegant way to define a pure virtual method in Python (2.7)? Again, the above solution only gives an error at run time.
  3. Is there a more elegant way to define a virtual method in Python (2.7)? No checking is needed, but there is no keyword like 'virtual' in Python afaik.

Thanks in advance.

like image 221
Michel Keijzers Avatar asked Jun 13 '13 13:06

Michel Keijzers


1 Answers

I'm not sure what exactly do you mean when you ask about virtual methods, as in Python all methods are virtual. And I'm also not sure why you are using _virtual_init instead of __init__… does simply renaming a method buy you something?

Update: Ah, I've got an idea. You probably did this to prohibit instantiation of your base class still keeping initialisation functionality in it. The answer here is: forcing things is not pythonic. Just document that this class is abstract (and probably call it AbstractSmth).

Speaking about run-times errors. Python is a dynamic language, so, well, that's how it works. A common idiom is raising NotImplementedError in __init__ to show that your class is abstract (and the same is true for abstract (pure virtual) methods). You can also have a look at abc as it does what you want: it prohibits instantiation of abstract classes. But I also strongly suggest reading PEP-3119 to understand what Abstract Base Classes are and what they are not.

AFAIK, raising NotImplementedError is enough for pylint to understand that your class/method is abstract.

like image 144
kirelagin Avatar answered Sep 27 '22 22:09

kirelagin