Class Inheritance Problem and __init__ Problem

Hi guys! Hope you all have a wonderful Thanksgiving!:tada::confetti_ball:
I have run into 2 new problems while working on ex45:
:small_orange_diamond:The first one is an inheritance problem. I want to create a “user” instance at the start of my game, and create a “monster” instance every time I have to. So I have the following code (only part of my code that is relevant):

class Scene(object):

    def __init__(self, user):
        self.user = user
        self.monster = MonGen()
        # generate a Monster instance

class LaserWeaponArmory(Scene):

    def print_func(self):
        print("LaserWeaponArmory")
        self.user.view_stat()
        self.monster.view_stat()
        # 2 view_stat() functions that I used to check stats of the user/monster,
        # they are under my other python file that already being imported 
        Combat(self.user, self.monster)
        # again, Combat is also being imported beforehand

class Map(object):

    map_num = [
       [ 0,  1,  2,  3],
       [10, 11, 12, 13],
       [20, 21, 22, 23],
       [30, 31, 32, 33]
    ]

    def __init__(self, a, b):
        self.a = a
        self.b = b
        self.start_num = self.map_num[a][b]
        self.user = UserGen()
        # generate a User instance at the beginning
        self.scene = Scene(self.user)
        # pass self.user to parent class Scene
        self.scenes = {
            1: LaserWeaponArmory(self.scene),
        }

However, I got the following error when it tries to call

self.user.view_stat()

AttributeError: ‘Scene’ object has no attribute ‘view_stat’

My thinking behind the code is that when I create a Map instance, the UserGen() will be called and generate a user only once at the beginning, then I can pass the user to my parent class Scene(object), and I call MonGen() under the parent Scene class so every time I enter a new child class scene(i.e LaserWeaponArmory), a new monster can be generated (MonGen() will be inherited and called), and the user will be inherited to the child scene to fight the monster.
My other parts of the code works perfectly, so I know there must be something wrong happened to my inheritance, but I don’t know which part goes wrong.:thinking:

:small_orange_diamond: The second one is an init() problem.
I want to do something like this to make my init looks simple:

__init__(self):
    self.a = a
    b, c, d = function_b()
    self.b = b
    self.c = c
    self.d = d

function_b(self):
    blah blah
    return b, c, d

However, error message tells me function_b is not defined. I’m wondering is it possible to call a function in init if the function is below init() function? I know I have to put init at the top of a class. Is there a way to achieve my goal here?:thinking:

For the second problem: If function_b is defined in the class body you need to call it as self.function_b() in __init__ (and any other method).

1 Like

For the first question: Could you post the whole code? Your reasoning makes sense to me and I can’t immediately see where things go awry.

from sys import exit
from random import randint
from textwrap import dedent

from ex45_combat import Combat
from ex45_character import UserGen, MonGen


class Scene(object):
    def __init__(self, user):
        self.user = user
        self.monster = MonGen()


class Death(Scene):
    def print_func(self):
        print("Death")


class CentralCorridor(Scene):
    def print_func(self):
        print("CentralCorridor")


class LaserWeaponArmory(Scene):
    def print_func(self):
        print("LaserWeaponArmory")
        self.user.view_stat()
        self.monster.view_stat()
        Combat(self.user, self.monster)


class TheBridge(Scene):
    def print_func(self):
        print("TheBridge")
        Combat(self.user, self.monster)


class EscapePod(Scene):
    def print_func(self):
        print("EscapePod")


class Finished(Scene):
    def print_func(self):
        print("Finished")


class Map(object):
    map_num = [
       [ 0,  1,  2,  3],
       [10, 11, 12, 13],
       [20, 21, 22, 23],
       [30, 31, 32, 33]
    ]

    def __init__(self, a, b):
        self.a = a
        self.b = b
        self.start_num = self.map_num[a][b]
        self.user = UserGen()
        self.scene = Scene(self.user)
        self.scenes = {
            0: CentralCorridor(self.scene),
            1: LaserWeaponArmory(self.scene),
            2: TheBridge(self.scene),
            12: EscapePod(self.scene),
            22: Death(self.scene),
            32: Finished(self.scene),
        }
        #print(self.start_num)

    def next_num(self, action):
        new_a = self.a
        new_b = self.b

        if action == 'North':
            new_a -= 1
        elif action == 'South':
            new_a += 1
        elif action == 'West':
            new_b -= 1
        elif action == 'East':
            new_b += 1
        else:
            print("Input Not Recognized. Please Re-Enter!")

        if new_a < 0 or new_a > 3 or new_b < 0 or new_b > 3:
            print("Out of Bound! Go Back!")
            return self.map_num[self.a][self.b]

        self.a = new_a
        self.b = new_b
        val = self.map_num[self.a][self.b]
        return val

    def opening_num(self):
        val = self.start_num
        return val

    def last_num(self, a, b):
        val = self.map_num[a][b]
        return val

This is the entire code from my ex45_map.py. Thank you so much for your help!!:heart::heart:
@florian

I see. I think you have not quite understood the concept of inheritance yet.

In your code LaserWeaponArmory inherits from Scene.

That means that when you create an instance of LaserWeaponArmory, the __init__ method of Scene will be used because you haven’t defined one for LaserWeaponArmory.

But it does not mean that when you create an instance of Scene all future instances of LaserWeaponArmory will inherit self.user from that instance of Scene. That’s not how it works.

In your case Scene is just a blueprint with common functions that all the actual scenes share. You don’t want to use it in your engine.

Here’s what I would do:

  1. Make sure you understand the difference between a class and an instance of a class. There’s a good explanation somewhere in the book.
  2. Then get rid of self.scene in Map.__init__. You don’t need it.
  3. For each of the scenes in self.scenes replace self.scene with self.user. Then the scene objects will be created with the __init__ method that they inherit from Scene and get their reference to the user object.

I hope this helps! It’s surprisingly difficult to write about it… Maybe @zedshaw will step in with a better explanation.

2 Likes

Your words are always helpful :+1::+1:. I’m more than happy to see your suggestions.
I’m still a little bit confused tbh, but I think I catch some of your points here. I originally didn’t set up a Scene class and pass self.user to each scenes as suggested. However, I decided to challenge myself with inheritance and it didn’t go well lol :hear_no_evil:.
I assume that every time I create an instance of child class (ie. LaserWeaponArmory()), I will get everything that from the parent class that’s not being overwritten by child class and everything new in the child class.
Now I do realize I’m passing self.user to the instance of the parent class, but not the class itself, which makes me thinking what should be done to the Scene class to achieve my original goal.:thinking: I created a USER instance under map function and what should I do to put the user instance to the Scene class so I can complete my inheritance?
I’ll go through the chapter again to see if I missed any important concepts. Thank you so much for your @florian help! Really appreciated!!

If the user instance is created when the game starts you can’t pass it on silently through inheritance. You have to pass a reference to it to the child instances when they’re created – i.e. to their init method, which happens to be one that they inherit from Scene.

A slightly cleaner way could be to define the scene mechanics in your engine/map, too, and only draw the descriptions from the scene objects. Thus you wouldn’t need to pass your user object to the individual scenes.

@CodeX I was making dinner, thinking about inheritance, and it suddenly occured to me what might have tripped you up.

Classes inherit from other classes: class Human(Ape):

Humans inherit many traits from apes. But does it make sense to say that Tom has inherited his red hair from the ugly orangutan in the local zoo?

Not quite. Instances of classes cannot inherit from other instances of other classes. That’s what you were trying to do with CentralCorrior(self.scene).

Consider this:

class Human(Ape):
    def __init__(self, name, age):
        self.name = name
        self.age = age

tom = Human(name="Tom", age=12)

class Human(Ape) starts a class definition: the stuff between parentheses is every old species that this class inherits from.

Human(name="Tom", age=12) is an object instantiation: the stuff between parentheses is what we pass to the class’ __init__ method in order to give this particular instance its individual attributes.

You cannot derive Human(bono_the_orangutan) from class Human(Ape) – class definition and object instantiation are very different things, although they look similar on first glance.

3 Likes

I think you provide a perfect example to explain the inheritance!! I now realized that I was actually inheriting “orangutan” to “Tom”, which is not the way inheritance meant to work.:joy:

However, I’m not sure about something here.
Do you mean that I could write a function under map that does what Scene class does, and get rid of the Scene class?
And what do you mean by

only draw the descriptions from the scene objects

I’m a little bit confused.:face_with_monocle:
Again, thank you so much for your help!:star:

1 Like

Yeah, I’m sorry @CodeX, that was not very clear advice.

My idea was that you could pull up the combat stuff from the scenes to some method in Map so you could easily refer to self.user. But on second thought that doesn’t seem much cleaner. Never mind.

Did you get your version to work?

lol, I followed my original way, which is to pass user to each individual child scene class and get rid of inheritance.:orangutan:

1 Like

You either have to pass data to everything, or create a “data base” that stores the entire world. Most other schemes end in failure of complexity.