Python Forum
Python inner classes inheritance from parent class
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Python inner classes inheritance from parent class
#1
Hello,

I am really struggling to understand the inheritance in python classes. In particular, I am trying to figure out how to pass the outer class attributes to inner class. Below is a sample code:

class Numbers:
   def __init__(self,a,b):
        self.a = a
        self.b = b

    class Operations:
        def __init__(self):
            Numbers.__init__(a,b)
            self.sum = self.a + self.b
            self.prod = self.a * self.b

twoNumbers = Numbers(5,4)

print(twoNumbers.Operations.sum)
When I run the code, I am getting an error:

Traceback (most recent call last):
  File "/Users/aabedin/Downloads/test_schedule.py", line 117, in <module>
    print(twoNumbers.Operations().sum)
          ^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/aabedin/Downloads/test_schedule.py", line 111, in __init__
    Numbers.__init__(a,b)
                     ^
NameError: name 'a' is not defined
My philosophy is as follows:
I create an object from the Numbers class. Inside that class there is another class called Operations which has certain attributes, corresponding to different arithmetic operations. I would imagine that accessing the attributes of the outer class should not be a problem, if the outer class Numbers has been instantiated inside the `__init__` method of the inner class, no?
Can you please help me understand how I can access the parent attributes within the child class?

P.S I am sorry of the formatting of the code is not right, but I don't know why "Insert ode snippet" does not show the formatting.
Reply
#2
Why 2 classes?

class Numbers:
    def __init__(self,a,b):
        self.sum = a + b
        self.prod = a * b
 
twoNumbers = Numbers(5,4)
 
print(twoNumbers.sum, twoNumbers.prod)
Reply
#3
(Apr-21-2025, 07:43 PM)Axel_Erfurt Wrote: Why 2 classes?

class Numbers:
    def __init__(self,a,b):
        self.sum = a + b
        self.prod = a * b
 
twoNumbers = Numbers(5,4)
 
print(twoNumbers.sum, twoNumbers.prod)

Thank you for the offered solution, but I am actually writing a more sophisticated code and I need to access the parent class attributes (not methods) within the child class. The snippet I posted is just an illustration of what I am trying to achieve. My actual code is very big and not suitable for posting here.
Reply
#4
I don't understand why you are defining a class inside another class. Doing so doesn't provide any kind of class relationship. Operations is not related to numbers in any way. The only thing the embedding does is force you to refer to the Numbers class to access the Operations class.
class Numbers:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    class Operations:
        def __init__(self, a, b):
            self.numbers = Numbers(a, b)
            self.sum = self.numbers.a + self.numbers.b
            self.prod = self.numbers.a * self.numbers.b

two_numbers = Numbers.Operations(5, 4)
print(two_numbers.sum, two_numbers.prod)
It actually makes more sense to flip it around.
class Operations:

    class Numbers:
        def __init__(self, a, b):
            self.a = a
            self.b = b

    def __init__(self, a, b):
        self.numbers = self.Numbers(a, b)
        self.sum = self.numbers.a + self.numbers.b
        self.prod = self.numbers.a * self.numbers.b

two_numbers = Operations(5, 4)
print(two_numbers.sum, two_numbers.prod)
Class relationships are IS A or HAS A. An "is a" relationship is where one class inherits attributes of another. This is called inheritance. The terms used to describe the rolls of the two classes are superclass and subclass, not parent and child.

A "has a" relationship is where one class has attributes that are instances of another class. Child and parent fit better with this type of relationship. Since everything in Python is an object of some class, even numbers, strings and lists, most python classes have a "has a" relationship with some other classes. Nothing special here.

In your example there is no relationship at all between the Number and Operation classes. They know nothing at all about each other. In my second example I convert this to a "has a" relationship when an Operations object has an attribute that is a Numbers object.

For completeness, below is an example of an "is a" relationship. Numbers is a class that holds two numbers. Operations is a class that inherits the number holding attributes of Numbers and adds new behaviors, computing the product and sum of those numbers.
class Numbers:
    def __init__(self, a, b):
        self.a = a
        self.b = b


class Operations(Numbers):
    def product(self):
        return self.a * self.b

    def sum(self):
        return self.a + self.b


ab = Operations(4, 5)
print(ab.product(), ab.sum())
Since you don't know how python classes work, maybe you should try describing in words, not code, what you are trying to achieve.
Reply
#5
(Apr-22-2025, 02:03 AM)deanhystad Wrote: I don't understand why you are defining a class inside another class. Doing so doesn't provide any kind of class relationship. Operations is not related to numbers in any way. The only thing the embedding does is force you to refer to the Numbers class to access the Operations class.
class Numbers:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    class Operations:
        def __init__(self, a, b):
            self.numbers = Numbers(a, b)
            self.sum = self.numbers.a + self.numbers.b
            self.prod = self.numbers.a * self.numbers.b

two_numbers = Numbers.Operations(5, 4)
print(two_numbers.sum, two_numbers.prod)
It actually makes more sense to flip it around.
class Operations:

    class Numbers:
        def __init__(self, a, b):
            self.a = a
            self.b = b

    def __init__(self, a, b):
        self.numbers = self.Numbers(a, b)
        self.sum = self.numbers.a + self.numbers.b
        self.prod = self.numbers.a * self.numbers.b

two_numbers = Operations(5, 4)
print(two_numbers.sum, two_numbers.prod)
Class relationships are IS A or HAS A. An "is a" relationship is where one class inherits attributes of another. This is called inheritance. The terms used to describe the rolls of the two classes are superclass and subclass, not parent and child.

A "has a" relationship is where one class has attributes that are instances of another class. Since everything in Python is an object of some class, even numbers, strings and lists, all python classes have a "has a" relationship with some other classes. Nothing special here.

In your example there is no relationship at all between the Number and Operation classes. They know nothing at all about each other. In my second example I convert this to a "has a" relationship when an Operations object has an attribute that is a Numbers object.

For completeness, below is an example of an "is a" relationship. Numbers is a class that holds two numbers. Product is a class that inherits the number holding attributes of Numbers and adds a new attribute, computing the product of those numbers.

Since you don't know how python classes work, maybe you should try describing in words, not code, what you are trying to achieve.



Pardon my ignorance on how classes and object-oriented programming works, as the only language I had previously programmed in was Fortran :) !

OK, I was able to extract an excerpt from my code (below), which doesn't work at the moment. I don't really think posting my entire code will help in any way.

import pandas as pd
import numpy as np

courseID = 'CHEM1250'

df = pd.DataFrame({'courseid':['CHEM1250', 'CHEM1250', 'CHEM1250', 'PHYS1200', 'PHYS1200', 'BIOL1150', 'BIOL1150'], \
    'coursesec':['L01', 'L02', 'L03', 'L01','L02','L01','L01'], \
    'starttime':['8:00', '12:00', 'NaN', '9:00', 'NaN', '8:00', '12:00'], \
    'endtime':['8:30', '12:30', '14:00', '10:00','12:00', '8:50', '13:50'], \
    'instructorid':['John', 'John', 'Bob', 'Jenna', 'Michel', 'Michel', 'Alice']})

class CourseSchedule:

    def __init__(self, DataFrame):
        self.df= DataFrame


    def _omit_nans(self, DataFrame):
        return DataFrame[DataFrame.notna()]


    class Course:

        def __init__(self, courseName):
            self.courseName = courseName
            self.show = self.display_schedule()

        def display_schedule(self):
            return CourseSchedule._omit_nans(df[df['courseid']==self.courseName])

schedule = CourseSchedule(df)
print(schedule.Course(courseID).show)
I am using a course schedule excel file as a DataFrame. I'd like to be able to create a single object out of the CourseSchedule class and then have different classes, constructed out of that original class. Inside each class, there will be methods, pertinent to that specific subclass. I also want to be able to define some parent class methods, so that I don't have to repeat code in each method of subclasses.
For example, using the schedule object, I'd like to have a Course class and inside it, there will be a number of methods pertinent to that Course class. The reason why I decided to go with a nested class inside the CourseSchedule class is because I want to logically and neatly group methods pertinent to that class. When I create the schedule object, I might want to do something with that Data Frame returned by schedule.Course('CHEM1250'). I think it is much cleaner if I was able to do schedule.Course('CHEM1250').show or e.g. schedule.Course('CHEM1250').take_smaller_subset and do something else with it. Similarly, inside the CourseSchedule, I'll have an Instructor class with its own methods, so I can do the following: schedule.Instructor('Bob').do_something. Hopefully, that explains what I am trying to achieve.
Reply
#6
The Zen of Python

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

It was already mentioned, but let me repeat - there is no benefit whatsoever in nesting classes like this. And this is not inheritance, nor parent/child relation, if this is what you think, based on your post title....
In my humble opinion your CourseShedule class is just a wrapper around a DataFrame and does not make much sense to have such class. It makes sense to have just Course class with all its attributes - schedule, instructor, etc. populated from a DataFrame at instantiation and respective methods.
Then hold all instances of Cource class in a list or similar structure OR create separate class, to hold instances of Course class e.g. in an attribute that is listr in itsel, but only if it provides any benefits
If you can't explain it to a six year old, you don't understand it yourself, Albert Einstein
How to Ask Questions The Smart Way: link and another link
Create MCV example
Debug small programs

Reply
#7
(Apr-22-2025, 04:16 AM)Abedin Wrote: I think it is much cleaner if I was able to do schedule.Course('CHEM1250').show or e.g. schedule.Course('CHEM1250').take_smaller_subset and do something else with it. Similarly, inside the CourseSchedule, I'll have an Instructor class with its own methods, so I can do the following: schedule.Instructor('Bob').do_something. Hopefully, that explains what I am trying to achieve.
Yes,but can do this in a flat structure without extra indentation or “inner” scopes.
Each class has a single responsibility.
Course and Instructor take exactly what they need (df plus an identifier).
No hidden links back to a parent instance—everything you need is passed in,then it will easier testing & reuse.
The it will be like this.
import pandas as pd
import numpy as np

class CourseSchedule:
    def __init__(self, df: pd.DataFrame):
        self.df = df

    def course(self, course_name: str):
        return Course(self.df, course_name)

    def instructor(self, instructor_id: str):
        return Instructor(self.df, instructor_id)


class Course:
    def __init__(self, df: pd.DataFrame, course_name: str):
        self.df = df
        self.course_name = course_name

    @property
    def show(self) -> pd.DataFrame:
        course_df = self.df[self.df['courseid'] == self.course_name]
        return course_df.dropna(subset=['starttime', 'endtime'])

    def take_smaller_subset(self, cols):
        return self.show.loc[:, cols]


class Instructor:
    def __init__(self, df: pd.DataFrame, instructor_id: str):
        self.df = df
        self.instructor_id = instructor_id

    @property
    def show(self) -> pd.DataFrame:
        instr_df = self.df[self.df['instructorid'] == self.instructor_id]
        return instr_df.dropna(subset=['starttime', 'endtime'])
Usage.
import pandas as pd
import numpy as np

df = pd.DataFrame({
    'courseid':    ['CHEM1250','CHEM1250','CHEM1250','PHYS1200','PHYS1200','BIOL1150','BIOL1150'],
    'coursesec':   ['L01','L02','L03','L01','L02','L01','L01'],
    'starttime':   ['8:00','12:00',    np.nan,'9:00',    np.nan,'8:00','12:00'],
    'endtime':     ['8:30','12:30','14:00','10:00','12:00','8:50','13:50'],
    'instructorid':['John','John','Bob','Jenna','Michel','Michel','Alice']
})

# instantiate the scheduler
schedule = CourseSchedule(df)
>>> chem = schedule.course('CHEM1250')
>>> chem.show
   courseid coursesec starttime endtime instructorid
0  CHEM1250       L01      8:00    8:30         John
1  CHEM1250       L02     12:00   12:30         John
>>> chem.take_smaller_subset(['coursesec','starttime'])
  coursesec starttime
0       L01      8:00
1       L02     12:00
>>> 
>>> 
>>> schedule.course('PHYS1200').show
   courseid coursesec starttime endtime instructorid
3  PHYS1200       L01      9:00   10:00        Jenna
>>> 
>>> schedule.instructor('Alice').show
   courseid coursesec starttime endtime instructorid
6  BIOL1150       L01     12:00   13:50        Alice
This flat, factory‑method approach keeps each class focused, and your usage always reads neatly as.
schedule.course(...).<something>
# or
schedule.instructor(...).<something>
Reply
#8
(Apr-22-2025, 03:00 PM)snippsat Wrote:
(Apr-22-2025, 04:16 AM)Abedin Wrote: I think it is much cleaner if I was able to do schedule.Course('CHEM1250').show or e.g. schedule.Course('CHEM1250').take_smaller_subset and do something else with it. Similarly, inside the CourseSchedule, I'll have an Instructor class with its own methods, so I can do the following: schedule.Instructor('Bob').do_something. Hopefully, that explains what I am trying to achieve.
Yes,but can do this in a flat structure without extra indentation or “inner” scopes.
Each class has a single responsibility.
Course and Instructor take exactly what they need (df plus an identifier).
No hidden links back to a parent instance—everything you need is passed in,then it will easier testing & reuse.
The it will be like this.
import pandas as pd
import numpy as np

class CourseSchedule:
    def __init__(self, df: pd.DataFrame):
        self.df = df

    def course(self, course_name: str):
        return Course(self.df, course_name)

    def instructor(self, instructor_id: str):
        return Instructor(self.df, instructor_id)


class Course:
    def __init__(self, df: pd.DataFrame, course_name: str):
        self.df = df
        self.course_name = course_name

    @property
    def show(self) -> pd.DataFrame:
        course_df = self.df[self.df['courseid'] == self.course_name]
        return course_df.dropna(subset=['starttime', 'endtime'])

    def take_smaller_subset(self, cols):
        return self.show.loc[:, cols]


class Instructor:
    def __init__(self, df: pd.DataFrame, instructor_id: str):
        self.df = df
        self.instructor_id = instructor_id

    @property
    def show(self) -> pd.DataFrame:
        instr_df = self.df[self.df['instructorid'] == self.instructor_id]
        return instr_df.dropna(subset=['starttime', 'endtime'])
Usage.
import pandas as pd
import numpy as np

df = pd.DataFrame({
    'courseid':    ['CHEM1250','CHEM1250','CHEM1250','PHYS1200','PHYS1200','BIOL1150','BIOL1150'],
    'coursesec':   ['L01','L02','L03','L01','L02','L01','L01'],
    'starttime':   ['8:00','12:00',    np.nan,'9:00',    np.nan,'8:00','12:00'],
    'endtime':     ['8:30','12:30','14:00','10:00','12:00','8:50','13:50'],
    'instructorid':['John','John','Bob','Jenna','Michel','Michel','Alice']
})

# instantiate the scheduler
schedule = CourseSchedule(df)
>>> chem = schedule.course('CHEM1250')
>>> chem.show
   courseid coursesec starttime endtime instructorid
0  CHEM1250       L01      8:00    8:30         John
1  CHEM1250       L02     12:00   12:30         John
>>> chem.take_smaller_subset(['coursesec','starttime'])
  coursesec starttime
0       L01      8:00
1       L02     12:00
>>> 
>>> 
>>> schedule.course('PHYS1200').show
   courseid coursesec starttime endtime instructorid
3  PHYS1200       L01      9:00   10:00        Jenna
>>> 
>>> schedule.instructor('Alice').show
   courseid coursesec starttime endtime instructorid
6  BIOL1150       L01     12:00   13:50        Alice
This flat, factory‑method approach keeps each class focused, and your usage always reads neatly as.
schedule.course(...).<something>
# or
schedule.instructor(...).<something>


Thank you so much for your help and providing a working example! I am really new to OOP and have yet learn the basics how different classes can work together. Your explanation was great and the example provided, clearly demonstrates that what I have been trying to achieve does not necessarily require a nesting classes. Just a comment or a question. In your example the function show"
@property
    def show(self) -> pd.DataFrame:
        instr_df = self.df[self.df['instructorid'] == self.instructor_id]
        return instr_df.dropna(subset=['starttime', 'endtime'])
[/python]

appears in both the Course and Instructor class, with the difference being the keys to sort the data frame. If I had 10 classes that will use the same function, does it mean that I have to put the same lines of code in all 10 classes? That seems like repeating code. I would imagine you could define it just once and reuse within other classes, with the corresponding parameters.

Right from the start, I knew I wanted to be able to access Instructor or Course methods as schedule.instructor('Bob').doSomething or schedule.course('Chem1205').doSomething, and while reading on the web about OOP I came across an article about nesting classes as a solution to what I was trying to do. However, your explanation and the comments of other users clearly demonstrated that nesting is not necessary. I definitely should read more about that. With all that said, I am really grateful to everyone in this community who has offered a solution and explanation how to achieve what I was going for.
I am sort of familiar with how to use Pd.DataFrame and some people may say "why do you need classes in the first place". Here is my why: The code I am writing will be potentially be used by other users and I find schedule.instructor('Bob').courses_taught much cleaner and easy to understand compared to schedule[schedule['instructorid']=='Bob']['courseid'] if I wanted to display the courses taught by 'Bob'. I'd be happy to hear your opinion if you think the latter looks cleaner to you :)
Reply
#9
(Apr-22-2025, 05:05 PM)Abedin Wrote: The code I am writing will be potentially be used by other users and I find schedule.instructor('Bob').courses_taught much cleaner and easy to understand compared to schedule[schedule['instructorid']=='Bob']['courseid'] if I wanted to display the courses taught by 'Bob'. I'd be happy to hear your opinion if you think the latter looks cleaner to you :)
It looks like you are building you own ORM (object-relational mapper) around a database that contains only one table.
« We can solve any problem by introducing an extra level of indirection »
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Python Classes rob101 4 1,533 Feb-05-2024, 06:51 PM
Last Post: rob101
  super() and order of running method in class inheritance akbarza 7 2,449 Feb-04-2024, 09:35 AM
Last Post: Gribouillis
Question [solved] Classes, assign an attributes to a class not to instances.. SpongeB0B 4 1,915 May-20-2023, 04:08 PM
Last Post: SpongeB0B
  Understanding Python classes PythonNewbee 3 2,154 Nov-10-2022, 11:07 PM
Last Post: deanhystad
Sad Python classes PythonNewbee 4 2,107 Nov-09-2022, 01:19 PM
Last Post: deanhystad
  Child class inheritance issue eakanathan 3 2,196 Apr-21-2022, 12:03 PM
Last Post: deanhystad
  dict class override: how access parent values? Andrey 1 2,537 Mar-06-2022, 10:49 PM
Last Post: deanhystad
  Subclass initialized property used in parent class method. Is it bad coding practice? saavedra29 5 3,679 Feb-07-2022, 07:29 PM
Last Post: saavedra29
  Inheritance vs Instantiation for Python classes mr_byte31 7 5,379 Oct-14-2021, 12:58 PM
Last Post: mr_byte31
  Understanding Python super() for classes OmegaRed94 1 2,450 Jun-09-2021, 09:02 AM
Last Post: buran

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020