inheritance in python 2.7.x

Learn inheritance in python 2.7.x with practical examples, diagrams, and best practices. Covers python, inheritance, super development techniques with visual explanations.

Mastering Inheritance in Python 2.7.x

Hero image for inheritance in python 2.7.x

Explore the fundamentals of inheritance in Python 2.7.x, including classic vs. new-style classes, method resolution order (MRO), and the use of super().

Inheritance is a fundamental concept in object-oriented programming (OOP) that allows a class (child or subclass) to inherit attributes and methods from another class (parent or superclass). This promotes code reusability and establishes a natural 'is-a' relationship between classes. While Python 3 has streamlined many aspects of inheritance, Python 2.7.x presents some nuances, particularly with 'classic' vs. 'new-style' classes and the super() function. Understanding these distinctions is crucial for writing robust and maintainable Python 2.7.x code.

Classic vs. New-Style Classes

In Python 2.7.x, there are two types of classes: classic classes and new-style classes. The distinction is important because it affects how inheritance, especially Method Resolution Order (MRO), behaves. New-style classes are the default in Python 3 and are generally preferred in Python 2.7.x as well, as they provide a more consistent and powerful object model.

A class becomes a new-style class by inheriting from object (either directly or indirectly). If a class does not explicitly inherit from object or another new-style class, it is considered a classic class. New-style classes introduce features like __slots__, descriptors, and a more predictable MRO based on the C3 linearization algorithm.

class ClassicClass:
    def __init__(self):
        print "Classic class initialized"

class NewStyleClass(object):
    def __init__(self):
        print "New-style class initialized"

# Example usage
classic_obj = ClassicClass()
new_style_obj = NewStyleClass()

Defining classic and new-style classes in Python 2.7.x

Method Resolution Order (MRO)

MRO is the order in which Python looks for a method in a hierarchy of classes. When you call a method on an object, Python needs to know which class's method to execute, especially in cases of multiple inheritance. The MRO determines this search path. For new-style classes, Python 2.7.x uses the C3 linearization algorithm, which provides a consistent and deterministic order.

You can inspect the MRO of a class using the __mro__ attribute or the mro() method. Understanding MRO is critical for debugging unexpected method calls in complex inheritance hierarchies.

class A(object):
    def method(self):
        print "A's method"

class B(A):
    def method(self):
        print "B's method"

class C(A):
    def method(self):
        print "C's method"

class D(B, C):
    def method(self):
        print "D's method"

print D.__mro__
# Expected output for new-style classes: (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)

Inspecting MRO for a new-style class with multiple inheritance

classDiagram
    class object
    class A
    class B
    class C
    class D

    object <|-- A
    A <|-- B
    A <|-- C
    B <|-- D
    C <|-- D

Class hierarchy for the MRO example

Using super() for Parent Method Calls

The super() function is used to call a method from a parent or sibling class. In Python 2.7.x, super() has a slightly different syntax compared to Python 3. It requires you to explicitly pass the current class and the instance as arguments: super(CurrentClass, self).method_name().

Using super() is crucial for cooperative multiple inheritance, ensuring that methods in the MRO are called correctly without explicit knowledge of the full inheritance chain. It helps avoid hardcoding parent class names, making your code more flexible and maintainable.

class Parent(object):
    def __init__(self, value):
        self.value = value
        print "Parent init with value: %s" % self.value

class Child(Parent):
    def __init__(self, value, extra):
        super(Child, self).__init__(value) # Call parent's __init__
        self.extra = extra
        print "Child init with extra: %s" % self.extra

    def display(self):
        print "Value: %s, Extra: %s" % (self.value, self.extra)

# Example usage
child_obj = Child(10, "hello")
child_obj.display()

Using super() in Python 2.7.x to call a parent's __init__ method