I have this project about using OOP in python. How to fully make use of OOP and use __init__(self)
?
class Parent:
name = "Player"
lives = 3
#display player running
def run(self, name):
self.name = name
print "%s is running." %self.name
#player will start to jump against enemy
def jump(self, name):
self.name = name
print "%s jumps over enemy. TOINK TOINK" % self.name
#player will start to grow
def grow(self, name):
self.name = name
print "%s sarting to Grow" %self.name
For player1
#Mario
class Child1(Parent):
name = "Mario"
#special skill
def blazingFire(self, name):
self.name = name
print "%s used his blazingFire" %self.name
For player 2
#Luigi
class Child2(Parent):
name = "Luigi"
#special Skill
def blazingWind(self,name):
self.name = name
print "%s used his blazingWind" %self.name
Calling object
player = Parent()
player1 = Child1()
player2 = Child2()
#mario's turn
player1.run(mario)
player1.jump(mario)
player1.grow(mario)
Am I doing the right thing or there's a better way for this?
2 Answers 2
This is clearly Python2. To have better inheritance in Python2 you have to inherit from
object
. Inheriting from object is classed as a new-style class, and is better than old ones.class Parent(object): pass class Child1(Parent): # No need to inherit object again. pass
You don't use
__init__
... In your example code it may be bad to use it, since you are adding spells anyway. And you're overwritingself.name
in your class anyway, so it's quite pointless.But lets say you wanted to have a default name for
Child1
, and you could assign it to something else, so you don't have to supply the name on every function. You can do almost exactly the same as you are doing on every function.def Parent(object): def __init__(self, name="Player", lives=3): self.name = name self.lives = lives def run(self): print "%s is running." % self.name
As for your print statements you should use
str.format
rather than the old 'sprintf'-like%
. There are quirks with%
andstr.format
is a lot stronger.print "{} is running.".format(self.name)
The above is exactly the same as your above prints, but I'm sure at one point you will want to print the name and the lives. You can change the contents of the
{}
's to allow you to use the name instead. Which allows for greater readability, as now we know '[player name] is running'.print "{0.name} is running.".format(self)
Why stop there you can show lives to!
print "{0.name} has {0.lives} live(s).".format(self)
As just saying 'use new style classes they are better than old style ones' isn't a compelling reason. There is something that you can do with new-style classes, which is calling it's parents functions. For example if we wanted to have the default name for
Child1
as Luigi, you can do the following:class Child1(Parent): def __init__(name="Luigi", **args): super(Child1, self).__init__(name=name, **args)
It's one good thing about new style classes, which get's improved in Python3 to:
class Child1(Parent): def __init__(name="Luigi", **args): super().__init__(name=name, **args)
This will now change the 'calling object' code to:
player1 = Child1('Jay')
player2 = Child1('Joe')
player1.run()
player1.jump()
player1.grow()
player2.run()
Here is Parent and Child1 with the above changes, note I removed all the self.name = name
's.
class Parent(object):
def __init__(name="Player", lives=3):
self.name = name
self.lives = lives
def run(self):
print "{0.name} is running.".format(self)
def jump(self):
print "{0.name} jumps over enemy. TOINK TOINK".format(self)
def grow(self):
print "{0.name} sarting to Grow".format(self)
class Child1(Parent):
def __init__(name="Luigi", **args):
super(Child1, self).__init__(name=name, **args)
def blazing_fire(self):
print "{0.name} used his blazingFire".format(self)
-
\$\begingroup\$ I find the
0.name
confusing personally, do you see it used instead offormat(self.name)
often? \$\endgroup\$SuperBiasedMan– SuperBiasedMan2015年12月11日 10:24:14 +00:00Commented Dec 11, 2015 at 10:24 -
\$\begingroup\$ @SuperBiasedMan I like
0.name
as then you don't have to care about placment, or doformat(name=self.name)
. But for a single varible it could very well be overkill... (I'm more thinking of when OP starts doing'{0.name} has {0.lives} live(s).'
) \$\endgroup\$2015年12月11日 10:35:47 +00:00Commented Dec 11, 2015 at 10:35 -
1\$\begingroup\$ I guess I personally like to do
"{} blah blah {}".format(self.name, self.lives)
even if it's more diverse. It's mostly style at that point though. \$\endgroup\$SuperBiasedMan– SuperBiasedMan2015年12月11日 10:39:03 +00:00Commented Dec 11, 2015 at 10:39
You definitely do need to use __init__
here. __init__
allows you to set attributes for a class that can then be used in functions or called on externally. It runs when a class is first created, so it's often used to set up the values that a class needs to run. Here's a very simple example:
class Player:
def __init__(self, name):
self.name = name
def print_name(self):
print("My name is " + self.name)
player1 = Player("Mario")
player2 = Player("Luigi")
print(player1.name)
# Mario
player2.print_name()
# My name is Luigi
You can see how this uses the self.name
set up you had, but you don't need to pass name
into the functions each time. Instead, when the class is created, you give it a name
value that gets stored in the class to be called in later. (note that you don't technically need to assign these values with __init__
, you could just do player1.name = "Mario"
but that's much less neat than using __init__
).
So in your case, let's set up your first class. I'd start by actually naming the class Player
, not just calling it Parent
and then giving it a name attribute that gets constantly overwritten. Then lives = 3
should also be in __init__
as putting it where you have it makes it a class attribute. Those are more commonly used for constants, or values shared across the whole class. __init__
is where you put the values that each individual instance of the class should have thir own value for:
class Player:
def __init__(self, name):
self.name = name
self.lives = 3
#display player running
def run(self):
print "%s is running." % self.name
#player will start to jump against enemy
def jump(self):
print "%s jumps over enemy. TOINK TOINK" % self.name
#player will start to grow
def grow(self):
print "%s starting to Grow" % self.name
Now you can see that you don't need to pass in name
any more because it's part of the class. It can easily reference it with self.name
instead. Also, I don't think you need separate classes for Mario and Luigi, since the only difference is that their function prints something different. Instead you could pass name
and skill
as two parameters and use them in the special_skill
function:
class Player:
def __init__(self, name, skill):
self.name = name
self.skill = skill
lives = 3
def special_skill(self):
print "%s used his %s" % (self.name, self.skill)
Some other notes. You should look into docstrings. They make functions clearer and easier to read. You have comments on each function already, so it's good to turn them into docstrings.
Instead of using %
to format, use str.format
which is the new style for formatting. It's cleaner and easier, and has other advantages for down the line. For example you could turn
print "%s used his %s" % (self.name, self.skill)
into
print "{} used his {}".format(self.name, self.skill)
-
\$\begingroup\$ Thanks guys I followed all your suggestions and working fine. \$\endgroup\$Jay Gorio– Jay Gorio2015年12月14日 01:50:50 +00:00Commented Dec 14, 2015 at 1:50