I'm new to programming and just learned about the concept of object oriented programming.
I'm trying to find a way to modify a existing instance make it belong to another class, by this I mean inheritance most of its property but not methods.
Like for us human to get a new job (have different method than the old one) but still is the same person (retain some property like age, name and so on).
I'm not good at describing my question, so I'll paste my example code down here.
from random import randrange
class human(object):
def __init__(self,name,age) -> None:
self.name= name
self.age = age
self.job = False
def introduce(self):
print(f"My name is {self.name}, I'm {self.age} years old now.")
def getjob(self,tfunc):
return tfunc(self.name,self.age)
class teacher(human):
def __init__(self,name,age) -> None:
self.name= name
self.age = age
self.job = 'teacher'
self.studentamount = randrange(12,24)
def introduce(self):
print(f"My name is {self.name}, I'm a {self.job} and have {self.studentamount} students. I'm {self.age} years old now.")
# imagine more method has same name as those in worker class down here.
class worker(human):
def __init__(self,name,age) -> None:
self.name= name
self.age = age
self.job = 'worker'
self.workhour = randrange(8,12)
def introduce(self):
print(f"My name is {self.name}, I'm a {self.job} and I work {self.workhour} hour per day. I'm {self.age} years old now.")
# imagine more method has same name as those in teacher class down here.
a = human('foo',31)
a.introduce()
a.age = 1
a = a.getjob(worker)
a.introduce()
a.age = 8
a = a.getjob(teacher)
a.introduce()
Output will be:
> My name is foo, I'm 31 years old now.
> My name is foo, I'm a worker and I work 9 hour per day. I'm 32 years old now.
> My name is foo, I'm a teacher and have 15 students. I'm 40 years old now.
Some backgrounds:
I was writing a "AI" for a minigame as coding practice, I want the "AI" have very different behavior sometimes.
Such as when take certain amount of damage become defensive which have different sets of movement and never think about attack at all, only 10 or so turns after will it fallback to the default behavior.
And I want to call instance.act() inside the game loop for every "AI", instead of instance.act_default() and instance.act_defensive().
So I think that so call "polymorphism" from what I just learn fit this perfectly, then I encountered this problem.
What I'm seeking is something like getjob() in my example code, but with less jank.
I would imagine when the class get more property, the getjob() method will become really clustered and unreadable and that's not what I want.
And maybe not creating a new human instance replacing the old one every time when that human gets a new job.
And maybe instead of a = a.getjob(teacher)
use something like a.getjob(teacher)
and self = tfunc(self.name,self.age)
inside getjob() method (which I try and will not work).
I edit this because my original question is too vague and not specific.
I change as little as I can, to describe what I'm trying to achieve more clearly. And I provide some background as I hope it would be helpful.
CodePudding user response:
When you have a subclass you can use super()
to use the superclass init to avoid code duplication. Also, now the if statement in introduce
has to check if job
attribute exists:
class human(object):
def __init__(self,name,age) -> None:
self.name= name
self.age = age
def introduce(self):
if hasattr(self, "job"):
print(f"My name is {self.name}, I'm a {self.job}, I'm {self.age} years old now.")
else:
print(f"My name is {self.name}, I'm {self.age} years old now.")
class teacher(human):
def __init__(self,name,age) -> None:
super().__init__(name, age)
self.job = 'teacher'
If you want to assign variables in getjob
more dynamically, you could use __dict__
to get all attributes as a dictionary from the class, but then you have to ignore the extra arguments. Use **
to unpack and catch unassign keyword arguments:
class human(object):
def __init__(self,name,age) -> None:
self.name= name
self.age = age
...
def getjob(self,tfunc):
return tfunc(**self.__dict__)
class teacher(human):
def __init__(self,name,age, **ignore) -> None:
super().__init__(name, age)
self.job = 'teacher'
However, I would instead make the "job" class an attribute of a human
. This way you don't have to create a new human each time they change jobs. If you think about it, changing jobs doesn't make you a different "human instance". Maybe something like:
class human(object):
def __init__(self,name,age,job=None) -> None:
self.name= name
self.age = age
self.job = job
steve = human("steve", 21)
steve.job = teacher()
bob = human("bob", 50, worker())