Posts: 8
Threads: 3
Joined: Apr 2025
Apr-21-2025, 06:40 PM
(This post was last modified: Apr-21-2025, 06:40 PM by Abedin.)
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.
Posts: 1,032
Threads: 16
Joined: Dec 2016
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)
Posts: 8
Threads: 3
Joined: Apr 2025
(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.
Posts: 6,798
Threads: 20
Joined: Feb 2020
Apr-22-2025, 02:03 AM
(This post was last modified: Apr-22-2025, 01:44 PM by deanhystad.)
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.
Posts: 8
Threads: 3
Joined: Apr 2025
(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.
Posts: 8,160
Threads: 160
Joined: Sep 2016
Apr-22-2025, 08:00 AM
(This post was last modified: Apr-22-2025, 08:00 AM by buran.)
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
Posts: 7,319
Threads: 123
Joined: Sep 2016
Apr-22-2025, 03:00 PM
(This post was last modified: Apr-22-2025, 03:00 PM by snippsat.)
(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>
Posts: 8
Threads: 3
Joined: Apr 2025
Apr-22-2025, 05:05 PM
(This post was last modified: Apr-22-2025, 05:05 PM by Abedin.)
(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 :)
Posts: 4,790
Threads: 76
Joined: Jan 2018
(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 »
|