is this the right way to do subclasses?

J

John Salerno

Ok, back to my so-called "game." I'm just curious if I've implemented
the subclasses properly, because it seems like an awful lot of
repetition with the parameters. And again, if I want to add a new
attribute later, I'd have to change a lot of things. I can't help but
get the feeling that I'm doing something very inefficiently.

Thanks!



class Character(object):

def __init__(self, name, strength, dexterity, intelligence):
self.name = name
self.health = 10
self.strength = strength
self.dexterity = dexterity
self.intelligence = intelligence


class Fighter(Character):

def __init__(self, name, strength, dexterity, intelligence):
Character.__init__(self, name, strength, dexterity, intelligence)
self.health += 2
self.strength += 1


class Thief(Character):

def __init__(self, name, strength, dexterity, intelligence):
Character.__init__(self, name, strength, dexterity, intelligence)
self.health += 1
self.dexterity += 1


class Mage(Character):

def __init__(self, name, strength, dexterity, intelligence):
Character.__init__(self, name, strength, dexterity, intelligence)
self.intelligence += 1
 
F

Farshid Lashkari

John said:
Ok, back to my so-called "game." I'm just curious if I've implemented
the subclasses properly, because it seems like an awful lot of
repetition with the parameters. And again, if I want to add a new
attribute later, I'd have to change a lot of things. I can't help but
get the feeling that I'm doing something very inefficiently.

Just accept variable arguments in the constructor of the sub-classes and
forward them to the base class.

class Fighter(Character):

def __init__(self, *args, **kw):
Character.__init__(self, *args, **kw)
self.health += 2
self.strength += 1

This way, if you add a new parameter to the base class, you won't need
to update all the derived classes.

-Farshid
 
P

Peter Otten

John said:
Ok, back to my so-called "game." I'm just curious if I've implemented
the subclasses properly, because it seems like an awful lot of
repetition with the parameters. And again, if I want to add a new
attribute later, I'd have to change a lot of things. I can't help but
get the feeling that I'm doing something very inefficiently.
class Character(object):

def __init__(self, name, strength, dexterity, intelligence):
self.name = name
self.health = 10
self.strength = strength
self.dexterity = dexterity
self.intelligence = intelligence


class Fighter(Character):

def __init__(self, name, strength, dexterity, intelligence):
Character.__init__(self, name, strength, dexterity, intelligence)
self.health += 2
self.strength += 1

One way to avoid the repetition:

class Character(object):
def __init__(self, name, strength, dexterity, intelligence):
self.name = name
self.health = 10
self.strength = strength
self.dexterity = dexterity
self.intelligence = intelligence
self.fix_attributes()

class Fighter(Character):
def fix_attributes(self):
self.health += 2
self.strength += 1

You may need a no-op implementation of fix_attributes() in the Character
class.

Peter
 
J

John Salerno

Peter said:
One way to avoid the repetition:

class Character(object):
def __init__(self, name, strength, dexterity, intelligence):
self.name = name
self.health = 10
self.strength = strength
self.dexterity = dexterity
self.intelligence = intelligence
self.fix_attributes()

class Fighter(Character):
def fix_attributes(self):
self.health += 2
self.strength += 1

You may need a no-op implementation of fix_attributes() in the Character
class.

Peter

But how does this take care of the initialization of the stats? I won't
be able to use Fighter to create a character without doing everything in
Character, right?
 
P

Peter Otten

But how does this take care of the initialization of the stats? I won't
be able to use Fighter to create a character without doing everything in
Character, right?

Try it: Fighter(...) will implicitly call Character.__init__(...).

Peter
 
G

Gabriel G

But how does this take care of the initialization of the stats? I won't
be able to use Fighter to create a character without doing everything in
Character, right?

Just try and see! :)
(Python is not Java, thanks Guido!)


--
Gabriel Genellina
Softlab SRL

__________________________________________________
Correo Yahoo!
Espacio para todos tus mensajes, antivirus y antispam ¡gratis!
¡Abrí tu cuenta ya! - http://correo.yahoo.com.ar
 
J

John Salerno

Peter said:
Try it: Fighter(...) will implicitly call Character.__init__(...).

Ok, I'm confused! :) I thought you had to explicity call a base class's
__init__ method? How is it doing this?
 
J

John Salerno

John said:
Ok, I'm confused! :) I thought you had to explicity call a base class's
__init__ method? How is it doing this?

Wait, I just might be an idiot. Is it not even necessary for me to call
Character's __init__ method? Fighter will inherit it automatically and
call it when a Fighter object is created, right?

If that's the case, then I don't know what I was thinking! I guess I was
thinking more along the lines of how in wxPython, when you subclass a
Frame, you have to call Frame.__init__, but maybe that's only because
you then do your own stuff in a custom __init__ class, which would have
otherwise overridden the Frame.__init__.

In this case, I need Character.__init__ and nothing else, right? Now
your other method is making sense to me too! :)
 
J

John Salerno

Peter said:
You may need a no-op implementation of fix_attributes() in the Character
class.

What does that mean? Is that in case I use Character directly to create
an object? Would that just be a 'pass' statement?
 
S

Steve Holden

John said:
Wait, I just might be an idiot. Is it not even necessary for me to call
Character's __init__ method? Fighter will inherit it automatically and
call it when a Fighter object is created, right?
By Jove, he's got it!
If that's the case, then I don't know what I was thinking! I guess I was
thinking more along the lines of how in wxPython, when you subclass a
Frame, you have to call Frame.__init__, but maybe that's only because
you then do your own stuff in a custom __init__ class, which would have
otherwise overridden the Frame.__init__.

In this case, I need Character.__init__ and nothing else, right? Now
your other method is making sense to me too! :)

Nice one.

regards
Steve
 
B

Bruno Desthuilliers

John Salerno a écrit :
Ok, back to my so-called "game." I'm just curious if I've implemented
the subclasses properly, because it seems like an awful lot of
repetition with the parameters. And again, if I want to add a new
attribute later, I'd have to change a lot of things. I can't help but
get the feeling that I'm doing something very inefficiently.

Thanks!



class Character(object):

def __init__(self, name, strength, dexterity, intelligence):
self.name = name
self.health = 10
self.strength = strength
self.dexterity = dexterity
self.intelligence = intelligence


class Fighter(Character):

def __init__(self, name, strength, dexterity, intelligence):
Character.__init__(self, name, strength, dexterity, intelligence)
self.health += 2
self.strength += 1


class Thief(Character):

def __init__(self, name, strength, dexterity, intelligence):
Character.__init__(self, name, strength, dexterity, intelligence)
self.health += 1
self.dexterity += 1


class Mage(Character):

def __init__(self, name, strength, dexterity, intelligence):
Character.__init__(self, name, strength, dexterity, intelligence)
self.intelligence += 1

This is correct, and a good first start. Now there's effectively some
boilerplate that we could get rid of using the template method pattern:

class Character(object):
def __init__(self, name, strength, dexterity, intelligence):
self.name = name
self.health = 10
self.strength = strength
self.dexterity = dexterity
self.intelligence = intelligence
self._post_init()

def _post_init(self):
pass

class Fighter(Character):
def _post_init(self):
self.health += 2
self.strength += 1


class Thief(Character):
def _post_init(self):
self.health += 1
self.dexterity += 1


class Mage(Character):
def _post_init(self):
self.intelligence += 1

HTH
 
B

Bruno Desthuilliers

John Salerno a écrit :
What does that mean? Is that in case I use Character directly to create
an object?

Most propbably it can be useful for other character classes that don't
need too fix attributes.
Would that just be a 'pass' statement?

Yes.
 
B

Ben Finney

John Salerno said:
Ok, back to my so-called "game." I'm just curious if I've
implemented the subclasses properly, because it seems like an awful
lot of repetition with the parameters. And again, if I want to add a
new attribute later, I'd have to change a lot of things. I can't
help but get the feeling that I'm doing something very
inefficiently.

This is another reason why passing the "stats" values as a mapping
object is a good alternative.
class Character(object):
def __init__(self, name, strength, dexterity, intelligence):
self.name = name
self.health = 10
self.strength = strength
self.dexterity = dexterity
self.intelligence = intelligence

class Character(object):
stat_keys = ['strength', 'dexterity', 'intelligence']
def __init__(self, name, stats):
self.name = name
self.health = 10
self.stats = {}
for (key, value) in [(k, stats.get(k)) for k in stat_keys]:
setattr(self, key, value)
class Fighter(Character):
def __init__(self, name, strength, dexterity, intelligence):
Character.__init__(self, name, strength, dexterity, intelligence)
self.health += 2
self.strength += 1

class Fighter(Character):
def __init__(self, name, stats):
Character.__init__(self, name, stats)
self.health += 1
self.strength += 1

et cetera.

All you need to do to add a new type of "stat" is to add a new item to
the 'stat_keys' list in Character.
 
G

Gabriel Genellina

class Character(object):
stat_keys = ['strength', 'dexterity', 'intelligence']
def __init__(self, name, stats):
self.name = name
self.health = 10
self.stats = {}
for (key, value) in [(k, stats.get(k)) for k in stat_keys]:
setattr(self, key, value)

Doesn't work, should say self.stat_keys. I prefer this other way:

=== cut ===
class Character(object):
stat_keys = ['strength', 'dexterity', 'intelligence']
strength = dexterity = intelligence = 0
def __init__(self, name, **stats):
self.name = name
self.health = 10
for key, value in stats.iteritems():
if key in self.stat_keys: setattr(self, key, value)
else: raise KeyError, "Unknown stats name: %s" % key

class Cleric(Character):
stat_keys = Character.stat_keys + ['holyness']
holyness = 0

c=Cleric('Fray Tuck', strength=8, dexterity=5, holyness=2)
print vars(c)
=== cut ===

This way, misspelled properties are caught, and calling interfase is
simple too. Also, class attributes provide default instance
attributes - you don't have to provide an explicit value. Stats names
are extensible as shown.


--
Gabriel Genellina
Softlab SRL

__________________________________________________
Correo Yahoo!
Espacio para todos tus mensajes, antivirus y antispam ¡gratis!
¡Abrí tu cuenta ya! - http://correo.yahoo.com.ar
 
J

John Salerno

Steve said:
By Jove, he's got it!

But I was thinking...even though there are better ways to do it than
what I did, I wasn't too far off the mark since I really *was*
overriding the __init__ and doing some extra initializing of my own in
each subclass. It was just a terribly ugly way to do it. :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,066
Latest member
VytoKetoReviews

Latest Threads

Top