Python naming conventions for attributes and methods meant to be overwritten

fortran picture fortran · Oct 18, 2011 · Viewed 7.9k times · Source

I have some object oriented code in Python, where some classes are meant to be extended to provide the missing custom bits of code (a la Template Method pattern, but also with variables), that will only be used by the super class, not by the client code using them.

Are there any style conventions for such abstract (or dull, because their implementation in the super class would be either pass or raise a NonImplemented exception) methods and attributes?

I've been browsing the PEP-0008 and it only mentions about prepending an underscore to private members not intended to be used by subclasses.

Answer

Anurag Uniyal picture Anurag Uniyal · Oct 18, 2011

I usually use single underscore e.g. _myvar for protected (as in C++) methods/attributes, which can be used by derived classes and use double underscore e.g. __var when it should not be used by anybody else, and as double-underscore names at class definition level are mangled, so they can't be overridden in derived class e.g.

class A(object):
    def result1(self): # public method
        return self._calc1()

    def result2(self): # public method
        return self.__calc2()

    def _calc1(self): # protected method, can be overridden in derived class
        return 'a1'

    def __calc2(self): # private can't be overridden
        return 'a2'


class B(A):
    def _calc1(self):
        return 'b1'

    def __calc2(self):
        return 'b2'

a = A()
print a.result1(),a.result2()
b = B()
print b.result1(),b.result2()

Here it seems derived class B is overriding both _calc1 and __calc2 but __calc2 isn't overridden because its name is already mangled with class name and hence output is

a1 a2
b1 a2

instead of

a1 a2
b1 b2

but ultimately choose any convention and document it, also in above case it is not that base class can't override private, here is a way :)

class B(A):
    def _calc1(self):
        return 'b1'

    def _A__calc2(self):
        return 'b2'