Example code - Inheritance issues: Difference between revisions

From 22118
Jump to navigation Jump to search
mNo edit summary
mNo edit summary
Line 120: Line 120:
     }
     }


# By changing instead of replacing we might get somewhere good.
# Thats OK, we will just add the tiger actions later - we inherited the add_action from CatClass
class Tiger(CatClass):
class Tiger(CatClass):
     acts['scratch'] = 'roars'
     pass
    acts['feed'] = 'demands more meat'
 
tigger = Tiger("Shere Khan", "male")
tigger.add_action('scratch', 'roars')
tigger.add_action('feed', 'demands more meat')
# Testing
tigger.action('scare')
tigger.action('feed')
# All good, but what happened to our cat?
mycat.action('scratch')
</pre>
</pre>
The cat started roaring when scratched - that is a tiger action. Looking at the add_action class method, it is not specifying anywhere that the CatClass should be used so what is wrong?

Revision as of 17:49, 19 March 2026

The challenges in inheritance can not be demonstrated well in a powerpoint. Thus, I made this page with examples. You can copy the code in the section and add it together and run it.

First I create a slightly bigger CatClass.

class CatClass:
    ### Class variables
    genus = "Felis"
    species = "Catus"
    acts = {'scratch': "purrs",
            'scare': "jumps high in the air",
            'feed': "is very excited" }

    ### Instatiation
    def __init__(self, name, gender):
        self.name, self.gender = name, gender

    ### Magic methods
    # Addition makes a kitten if male and female cat, alternative constructor
    def __add__(self, other):
        # Taken a short cut and just demanding the genders to be different to breed
        if self.gender == other.gender:
            return NotImplemented
        # Being biased and saying all kittens are male
        kitten = CatClass('Unnamed kitten', 'male')
        return kitten     
        
    ### Class methods
    # Adding a cat action
    @classmethod
    def add_action(cls, action, reaction):
       cls.acts[action] = reaction
    # Querying class for number of actions
    @classmethod
    def count_action(cls):
        return len(cls.acts)

    ### Property methods
    # age setter and getter
    @property
    def age(self):
        if not hasattr(self, '_age'):
            raise NameError("Age of cat is not yet set")
        return self._age
        
    @age.setter
    def age(self, value):
        if value < 0:
            raise ValueError("Age can not be negative")
        self._age = value

    ### Instance methods
    # The action method - using class variables
    def action(self, act):
        if act in CatClass.acts:
            print(self.name, CatClass.acts[act])
        elif self.gender == 'male':
            print(self.name, "looks bored")
        else:
            print(self.name, "yawns")

### Main program                    
yourcat = CatClass('Missy', 'female')
yourcat.age = 5
print(f"The age of your cat is {yourcat.age}")

CatClass.add_action("annoy", "scratches your face")
print("Actions in CatClass:", CatClass.count_action())

mycat = CatClass("Tom", "male")
kitten = mycat + yourcat

print(f"Kitten: {kitten.name}")

yourcat.action('scare')

This class looks fine, but it has a number of inheritance issues. By creating a Tiger class that inherits from the CatClass, I will demonstrate. Also, see how simple it was to create a new class based on an old :-)

class Tiger(CatClass):
    acts = {
        'scratch': 'roars',
        'feed': 'demands more meat'
    }

tigger = Tiger("Shere Khan", "male")
tigger.action("scratch")
tigger.action('scare')

We want the Tiger to react differently than the Cat, at least for some actions. However, this code does not do it. The problem lies in the inherited action method.

    def action(self, act):
        if act in CatClass.acts:
            print(self.name, CatClass.acts[act])
        elif self.gender == 'male':
            print(self.name, "looks bored")
        else:
            print(self.name, "yawns")

    # We are specifically using the actions in the CatClass for the behavior
    # We need to use the current class, as in cats uses CatClass actions and tigers uses Tiger actions.

    # These are the problematic lines, they can be replaced in different ways
        if act in CatClass.acts:
            print(self.name, CatClass.acts[act])
    # Through '''self''', as self will search the instance namespace and then the class namespace for the variable
        if act in self.acts:
            print(self.name, self.acts[act])
    # By dynamically getting the class by type
        cls = type(self)
        if act in cls.acts:
            print(self.name, cls.acts[act])

This does not completely solve the problem. We would still like to be able to scare the tiger (inherit the cat action), but for now it looks bored when we try. This problem is caused by the complete replacement of the class variable acts in the Tiger class. The cat actions are deleted and replaced with tiger-only actions.

class Tiger(CatClass):
    acts = {
        'scratch': 'roars',
        'feed': 'demands more meat'
    }

# Thats OK, we will just add the tiger actions later - we inherited the add_action from CatClass
class Tiger(CatClass):
    pass

tigger = Tiger("Shere Khan", "male")
tigger.add_action('scratch', 'roars')
tigger.add_action('feed', 'demands more meat')
# Testing
tigger.action('scare')
tigger.action('feed')
# All good, but what happened to our cat?
mycat.action('scratch')

The cat started roaring when scratched - that is a tiger action. Looking at the add_action class method, it is not specifying anywhere that the CatClass should be used so what is wrong?