Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #7882 > unrolled thread
| Started by | John Salerno <johnjsal@gmail.com> |
|---|---|
| First post | 2011-06-17 21:17 -0700 |
| Last post | 2011-06-20 13:58 -0400 |
| Articles | 17 — 11 participants |
Back to article view | Back to comp.lang.python
What's the best way to write this base class? John Salerno <johnjsal@gmail.com> - 2011-06-17 21:17 -0700
Re: What's the best way to write this base class? Chris Angelico <rosuav@gmail.com> - 2011-06-18 14:53 +1000
Re: What's the best way to write this base class? "bruno.desthuilliers@gmail.com" <bruno.desthuilliers@gmail.com> - 2011-06-18 03:55 -0700
Re: What's the best way to write this base class? Tim Chase <python.list@tim.thechases.com> - 2011-06-18 06:24 -0500
Re: What's the best way to write this base class? "bruno.desthuilliers@gmail.com" <bruno.desthuilliers@gmail.com> - 2011-06-18 06:37 -0700
Re: What's the best way to write this base class? Ian Kelly <ian.g.kelly@gmail.com> - 2011-06-18 08:51 -0600
Re: What's the best way to write this base class? TheSaint <nobody@nowhere.net.no> - 2011-06-18 19:04 +0800
Re: What's the best way to write this base class? Mel <mwilson@the-wire.com> - 2011-06-18 10:22 -0400
Re: What's the best way to write this base class? Ethan Furman <ethan@stoneleaf.us> - 2011-06-18 08:37 -0700
Re: What's the best way to write this base class? John Salerno <johnjsal@gmail.com> - 2011-06-18 09:26 -0700
Re: What's the best way to write this base class? Chris Angelico <rosuav@gmail.com> - 2011-06-19 02:34 +1000
Re: What's the best way to write this base class? Chris Kaynor <ckaynor@zindagigames.com> - 2011-06-19 18:52 -0700
Re: What's the best way to write this base class? John Salerno <johnjsal@gmail.com> - 2011-06-19 21:04 -0700
Re: What's the best way to write this base class? Benjamin Kaplan <benjamin.kaplan@case.edu> - 2011-06-20 00:12 -0700
Re: What's the best way to write this base class? Mel <mwilson@the-wire.com> - 2011-06-20 07:57 -0400
Re: What's the best way to write this base class? Ian Kelly <ian.g.kelly@gmail.com> - 2011-06-20 12:31 -0600
Re: What's the best way to write this base class? Terry Reedy <tjreedy@udel.edu> - 2011-06-20 13:58 -0400
| From | John Salerno <johnjsal@gmail.com> |
|---|---|
| Date | 2011-06-17 21:17 -0700 |
| Subject | What's the best way to write this base class? |
| Message-ID | <142e76c3-b304-43ef-af24-919fa6146369@c9g2000yqp.googlegroups.com> |
Let's say I'm writing a game (really I'm just practicing OOP) and I
want to create a "Character" base class, which more specific classes
will subclass, such as Warrior, Wizard, etc. Which of the following
ways is better, or is there another way?
Note: I have in mind that when a specific subclass (Warrior, Wizard,
etc.) is created, the only argument that will ever be passed to the
__init__ method is the name. The other variables will never be
explicitly passed, but will be set during initialization. With that in
mind, here are the ways I've come up with:
1)
class Character:
def __init__(self, name, base_health=50, base_resource=10):
self.name = name
self.health = base_health
self.resource = base_resource
2)
class Character:
base_health = 50
base_resource = 10
def __init__(self, name):
self.name = name
self.health = base_health
self.resource = base_resource
3)
BASE_HEALTH = 50
BASE_RESOURCE = 10
class Character:
def __init__(self, name):
self.name = name
self.health = BASE_HEALTH
self.resource = BASE_RESOURCE
[toc] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2011-06-18 14:53 +1000 |
| Message-ID | <mailman.109.1308372833.1164.python-list@python.org> |
| In reply to | #7882 |
On Sat, Jun 18, 2011 at 2:17 PM, John Salerno <johnjsal@gmail.com> wrote: > 1) > class Character: > > def __init__(self, name, base_health=50, base_resource=10): > self.name = name > self.health = base_health > self.resource = base_resource If you expect to override the health/resource, I'd use this model. ChrisA
[toc] | [prev] | [next] | [standalone]
| From | "bruno.desthuilliers@gmail.com" <bruno.desthuilliers@gmail.com> |
|---|---|
| Date | 2011-06-18 03:55 -0700 |
| Message-ID | <e6841cf8-f365-4c23-9307-b69565025203@dq9g2000vbb.googlegroups.com> |
| In reply to | #7882 |
On 18 juin, 06:17, John Salerno <johnj...@gmail.com> wrote:
> Note: I have in mind that when a specific subclass (Warrior, Wizard,
> etc.) is created, the only argument that will ever be passed to the
> __init__ method is the name. The other variables will never be
> explicitly passed, but will be set during initialization.
__init__ is actually supposed to be the initialization phase, but well
<g>
> 1)
> class Character:
If you using Python 2.x, make this:
class Character(object):
> def __init__(self, name, base_health=50, base_resource=10):
> self.name = name
> self.health = base_health
> self.resource = base_resource
If neither base_health nor base_resource are supposed to be passed in,
why make them arguments at all:
class Character(object):
def __init__(self, name):
self.name = name
self.health = 50
self.resource = 10
> 2)
> class Character:
>
> base_health = 50
> base_resource = 10
>
> def __init__(self, name):
> self.name = name
> self.health = base_health
> self.resource = base_resource
Did you at least tried this one ? Hint: it won't work.
> 3)
> BASE_HEALTH = 50
> BASE_RESOURCE = 10
>
> class Character:
>
> def __init__(self, name):
> self.name = name
> self.health = BASE_HEALTH
> self.resource = BASE_RESOURCE
This is probably what I'd do.
[toc] | [prev] | [next] | [standalone]
| From | Tim Chase <python.list@tim.thechases.com> |
|---|---|
| Date | 2011-06-18 06:24 -0500 |
| Message-ID | <mailman.116.1308396299.1164.python-list@python.org> |
| In reply to | #7896 |
On 06/18/2011 05:55 AM, bruno.desthuilliers@gmail.com wrote:
> On 18 juin, 06:17, John Salerno<johnj...@gmail.com> wrote:
>> class Character:
>>
>> base_health = 50
>> base_resource = 10
>>
>> def __init__(self, name):
>> self.name = name
>> self.health = base_health
>> self.resource = base_resource
>
> Did you at least tried this one ? Hint: it won't work.
If you want it, you can use
self.health = Character.base_health
Though I'd treat them as semi-constants and capitalize them like
your 3rd case:
class Character(object):
BASE_HEALTH = 50
...
def __init__(...):
...
self.health = Character.BASE_HEALTH
-tkc
[toc] | [prev] | [next] | [standalone]
| From | "bruno.desthuilliers@gmail.com" <bruno.desthuilliers@gmail.com> |
|---|---|
| Date | 2011-06-18 06:37 -0700 |
| Message-ID | <6c64a047-f137-4865-9dde-812461a4090e@u10g2000yqh.googlegroups.com> |
| In reply to | #7899 |
On 18 juin, 13:24, Tim Chase <python.l...@tim.thechases.com> wrote:
> On 06/18/2011 05:55 AM, bruno.desthuilli...@gmail.com wrote:
>
> > On 18 juin, 06:17, John Salerno<johnj...@gmail.com> wrote:
> >> class Character:
>
> >> base_health = 50
> >> base_resource = 10
>
> >> def __init__(self, name):
> >> self.name = name
> >> self.health = base_health
> >> self.resource = base_resource
>
> > Did you at least tried this one ? Hint: it won't work.
>
> If you want it, you can use
>
> self.health = Character.base_health
>
> Though I'd treat them as semi-constants and capitalize them like
> your 3rd case:
>
> class Character(object):
> BASE_HEALTH = 50
> ...
> def __init__(...):
> ...
> self.health = Character.BASE_HEALTH
>
If you go that way, then using polymorphic dispatch might (or not,
depending on the game's rules <g>) be a good idea:
class Character(object):
BASE_HEALTH = 50
...
def __init__(self, name):
...
self.health = type(self).BASE_HEALTH
This would allow different Character subclasses to have different
BASE_HEALTH etc..., defaulting to the base class values.
[toc] | [prev] | [next] | [standalone]
| From | Ian Kelly <ian.g.kelly@gmail.com> |
|---|---|
| Date | 2011-06-18 08:51 -0600 |
| Message-ID | <mailman.121.1308408694.1164.python-list@python.org> |
| In reply to | #7905 |
On Sat, Jun 18, 2011 at 7:37 AM, bruno.desthuilliers@gmail.com <bruno.desthuilliers@gmail.com> wrote: > If you go that way, then using polymorphic dispatch might (or not, > depending on the game's rules <g>) be a good idea: > > > class Character(object): > BASE_HEALTH = 50 > ... > def __init__(self, name): > ... > self.health = type(self).BASE_HEALTH This of course is equivalent to a simple "self.health = self.BASE_HEALTH" as long as you haven't explicitly assigned BASE_HEALTH on the instance. Tangentially, I wouldn't use inheritance at all for this game. I know the classic "is-a / has-a" test says that a wizard "is a" character, but in my experience that method leans toward doing way too much inheritance. If you have subclasses for character classes, then you will be tempted to also use subclasses for races, and then when you're ready to make elf wizards you will have forced yourself into a multiple inheritance situation, and down that path wait Agony and Despair. Instead, I would use composition here. A character has a class (e.g. Wizard(specialization='fire')) and a race (e.g. Elf(breed='high') -- or maybe just HighElf(), which inherits from Elf). Save inheritance for broad categories of what it means to be a character (e.g. PlayerCharacter vs. NonPlayerCharacter or MobileCharacter vs. MagicMirror, etc., any of which might have the Wizard character class). Cheers, Ian
[toc] | [prev] | [next] | [standalone]
| From | TheSaint <nobody@nowhere.net.no> |
|---|---|
| Date | 2011-06-18 19:04 +0800 |
| Message-ID | <iti0na$q3g$1@speranza.aioe.org> |
| In reply to | #7882 |
John Salerno wrote: > class Character: I'd vote to point 1 -- goto /dev/null
[toc] | [prev] | [next] | [standalone]
| From | Mel <mwilson@the-wire.com> |
|---|---|
| Date | 2011-06-18 10:22 -0400 |
| Message-ID | <iticc1$o12$1@speranza.aioe.org> |
| In reply to | #7882 |
John Salerno wrote:
[ ... ]
> 1)
> class Character:
> def __init__(self, name, base_health=50, base_resource=10):
> self.name = name
> self.health = base_health
> self.resource = base_resource
>
> 2)
> class Character:
> base_health = 50
> base_resource = 10
> def __init__(self, name):
> self.name = name
> self.health = base_health
> self.resource = base_resource
>
> 3)
> BASE_HEALTH = 50
> BASE_RESOURCE = 10
> class Character:
> def __init__(self, name):
> self.name = name
> self.health = BASE_HEALTH
> self.resource = BASE_RESOURCE
For completeness, there's also 4)
Python 2.6.5 (r265:79063, Apr 16 2010, 13:09:56)
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> class Character (object):
... health = 50
... def __init__ (self, name):
... self.name = name
... print self.name, self.health
...
>>> Character ('Eunice')
Eunice 50
where the class attribute is used until it's overridden in the instance.
Mel.
[toc] | [prev] | [next] | [standalone]
| From | Ethan Furman <ethan@stoneleaf.us> |
|---|---|
| Date | 2011-06-18 08:37 -0700 |
| Message-ID | <mailman.122.1308411487.1164.python-list@python.org> |
| In reply to | #7882 |
John Salerno wrote:
> 1)
> class Character:
>
> def __init__(self, name, base_health=50, base_resource=10):
> self.name = name
> self.health = base_health
> self.resource = base_resource
You said above that health and resource will never be explicitly passed,
yet here you have allowed for that possibility. If you are going to
have mosters, etc, also inherit from Character, with different health
and resources, I would go this route with one change:
def __init__(self, name, base_health, base_resoures):
and always specify those numbers on creation.
> 2)
> class Character:
>
> base_health = 50
> base_resource = 10
>
> def __init__(self, name):
> self.name = name
> self.health = base_health
> self.resource = base_resource
You do not need to assign health and resource here -- they are already
assigned on the class, so the instance will see them automatically.
When a change is made, the instance will automagically get its own copy.
> 3)
> BASE_HEALTH = 50
> BASE_RESOURCE = 10
>
> class Character:
>
> def __init__(self, name):
> self.name = name
> self.health = BASE_HEALTH
> self.resource = BASE_RESOURCE
If *all* characters (player, non-player, monster, etc) will have the
same base health and resources then this is fine -- otherwise I would
use option 1.
~Ethan~
[toc] | [prev] | [next] | [standalone]
| From | John Salerno <johnjsal@gmail.com> |
|---|---|
| Date | 2011-06-18 09:26 -0700 |
| Message-ID | <fa2ee2a4-f46f-42c7-846c-b854977f9e38@16g2000yqy.googlegroups.com> |
| In reply to | #7912 |
Whew, thanks for all the responses! I will think about it carefully
and decide on a way. I was leaning toward simply assigning the health,
resource, etc. variables in the __init__ method, like this:
def __init__(self, name):
self.name = name
self.health = 50
self.resource = 10
I never did like the idea of using the parameters if I never intended
to pass them in...just seems wrong. :)
The idea of not using a base Character class at all threw me for a
loop though, so I need to think about that too!
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2011-06-19 02:34 +1000 |
| Message-ID | <mailman.124.1308414900.1164.python-list@python.org> |
| In reply to | #7913 |
On Sun, Jun 19, 2011 at 2:26 AM, John Salerno <johnjsal@gmail.com> wrote: > The idea of not using a base Character class at all threw me for a > loop though, so I need to think about that too! > It's easy to fall in love with a concept like inheritance, and use it in all sorts of things. You then have a choice to make: Is the project you're writing primarily for its own sake, or primarily so that you can explore the programming concept? There's nothing wrong with building a mediocre game on a mediocre basis and using it solely to play around with OO and inheritance and class structures, but if you want it to be a good game, sometimes you need to go back on decisions like that. And that's where mailing lists like this are awesome. I've learned so much from the wisdom here... there is an amazing amount of expertise being offered freely! Chris Angelico
[toc] | [prev] | [next] | [standalone]
| From | Chris Kaynor <ckaynor@zindagigames.com> |
|---|---|
| Date | 2011-06-19 18:52 -0700 |
| Message-ID | <mailman.166.1308534779.1164.python-list@python.org> |
| In reply to | #7913 |
On Jun 18, 2011, at 9:26, John Salerno <johnjsal@gmail.com> wrote: > Whew, thanks for all the responses! I will think about it carefully > and decide on a way. I was leaning toward simply assigning the health, > resource, etc. variables in the __init__ method, like this: > > def __init__(self, name): > self.name = name > self.health = 50 > self.resource = 10 > > I never did like the idea of using the parameters if I never intended > to pass them in...just seems wrong. :) > > The idea of not using a base Character class at all threw me for a > loop though, so I need to think about that too! Having a character class (along with possibly player character, non-player character, etc), make sense; however you probably want to make stuff like health, resources, damage, and any other attributes not be handles by any classes or inheritance in order to allow you to make such data-driven (ie, read from a file). Doing so makes the game much more extendable: using classes, you are likely limited to 5 or 'combinations and a few developers (plus, any designers need to know programming). A basic way to determine between using subclasses over a data driven approach is: is there significantly different back-end behavior or merely attribute differences. > -- > http://mail.python.org/mailman/listinfo/python-list
[toc] | [prev] | [next] | [standalone]
| From | John Salerno <johnjsal@gmail.com> |
|---|---|
| Date | 2011-06-19 21:04 -0700 |
| Message-ID | <5c8be025-2d2c-42fc-a764-bd1ca03ba398@d14g2000yqb.googlegroups.com> |
| In reply to | #7995 |
On Jun 19, 8:52 pm, Chris Kaynor <ckay...@zindagigames.com> wrote: > Having a character class (along with possibly player character, non-player character, etc), make sense; however you probably want to make stuff like health, resources, damage, and any other attributes not be handles by any classes or inheritance in order to allow you to make such data-driven (ie, read from a file). Doing so makes the game much more extendable: using classes, you are likely limited to 5 or 'combinations and a few developers (plus, any designers need to know programming). > > A basic way to determine between using subclasses over a data driven approach is: is there significantly different back-end behavior or merely attribute differences. Can you give a basic example of how this data-driven approach would work? You don't have to provide any code, just a description would be helpful. Such as, do I create a data file per character, and then have each character instance read/write to that file? Is it good to have so many files open at once, or would they only need to be read, closed, then opened again at the end to write?
[toc] | [prev] | [next] | [standalone]
| From | Benjamin Kaplan <benjamin.kaplan@case.edu> |
|---|---|
| Date | 2011-06-20 00:12 -0700 |
| Message-ID | <mailman.177.1308554162.1164.python-list@python.org> |
| In reply to | #7996 |
On Sun, Jun 19, 2011 at 9:04 PM, John Salerno <johnjsal@gmail.com> wrote:
> On Jun 19, 8:52 pm, Chris Kaynor <ckay...@zindagigames.com> wrote:
>
>> Having a character class (along with possibly player character, non-player character, etc), make sense; however you probably want to make stuff like health, resources, damage, and any other attributes not be handles by any classes or inheritance in order to allow you to make such data-driven (ie, read from a file). Doing so makes the game much more extendable: using classes, you are likely limited to 5 or 'combinations and a few developers (plus, any designers need to know programming).
>>
>> A basic way to determine between using subclasses over a data driven approach is: is there significantly different back-end behavior or merely attribute differences.
>
> Can you give a basic example of how this data-driven approach would
> work? You don't have to provide any code, just a description would be
> helpful. Such as, do I create a data file per character, and then have
> each character instance read/write to that file? Is it good to have so
> many files open at once, or would they only need to be read, closed,
> then opened again at the end to write?
> --
I'm pretty sure he means that if the only difference between classes
is configuration (i.e. you aren't actually going to change code
between character classes, just base stats, growth rates, and a list
of available skills or something of that nature), then you should
store the configurations in a config file rather than making a new
class. So rather than having
class WizardCharacter(Character) :
base_health = 50
...
class WarriorCharacter(Character) :
base_health=70
...
You make a config file
--- characterclasses.ini ---
[Wizard]
base_health=50
[Warrior]
base_health=70
Then, when you make a new character, rather than doing a
WizardCharacter() or a WarriorCharacter(), you do a
Character(job='Wizard') and then look up the various defaults in your
config file. Doing it this way makes it trivial to add a new class. If
you want to use an old-fashioned INI file, you can use the
ConfigParser class to read them. If you want to nest attributes (for
instance, a list of sub-items), you'll probably want to go with XML
and ElementTree. I guess you can also use JSON (which uses a syntax
similar to Python's dictionaries) but I've never really tried to make
one of those by hand before so I'm not sure how well it will work out.
[toc] | [prev] | [next] | [standalone]
| From | Mel <mwilson@the-wire.com> |
|---|---|
| Date | 2011-06-20 07:57 -0400 |
| Message-ID | <itncj3$s1g$1@speranza.aioe.org> |
| In reply to | #7996 |
John Salerno wrote: > On Jun 19, 8:52 pm, Chris Kaynor <ckay...@zindagigames.com> wrote: > >> Having a character class (along with possibly player character, >> non-player character, etc), make sense; however you probably want to make >> stuff like health, resources, damage, and any other attributes not be >> handles by any classes or inheritance in order to allow you to make such >> data-driven (ie, read from a file). Doing so makes the game much more >> extendable: using classes, you are likely limited to 5 or 'combinations >> and a few developers (plus, any designers need to know programming). >> >> A basic way to determine between using subclasses over a data driven >> approach is: is there significantly different back-end behavior or merely >> attribute differences. > > Can you give a basic example of how this data-driven approach would > work? You don't have to provide any code, just a description would be > helpful. Such as, do I create a data file per character, and then have > each character instance read/write to that file? Is it good to have so > many files open at once, or would they only need to be read, closed, > then opened again at the end to write? Battle for Wesnoth is set up this way. I don't know what the code does, but you can go wild creating new classes of character by mixing up new combinations of attribute settings in new configuration files, and injecting them into the standard game config files. AFAIK you are stuck with the attributes the game is programmed for. I've seen no way to create a new dimension for the game -- Conversation, for instance, with currently unknown attributes like vocabulary or tone. Mel.
[toc] | [prev] | [next] | [standalone]
| From | Ian Kelly <ian.g.kelly@gmail.com> |
|---|---|
| Date | 2011-06-20 12:31 -0600 |
| Message-ID | <mailman.188.1308594707.1164.python-list@python.org> |
| In reply to | #8012 |
On Mon, Jun 20, 2011 at 5:57 AM, Mel <mwilson@the-wire.com> wrote: > Battle for Wesnoth is set up this way. I don't know what the code does, but > you can go wild creating new classes of character by mixing up new > combinations of attribute settings in new configuration files, and injecting > them into the standard game config files. > > AFAIK you are stuck with the attributes the game is programmed for. I've > seen no way to create a new dimension for the game -- Conversation, for > instance, with currently unknown attributes like vocabulary or tone. The Dwarf Fortress data files are also well worth taking a look at in this regard. Virtually everything is configurable, from basic attributes like size and language all the way down to intricate details like types and quantity of body parts, what tissues said body parts are made of and what roles they play. To get an idea of the level of customization possible have a look at: http://df.magmawiki.com/index.php/Modding#Modding_the_creatures
[toc] | [prev] | [next] | [standalone]
| From | Terry Reedy <tjreedy@udel.edu> |
|---|---|
| Date | 2011-06-20 13:58 -0400 |
| Message-ID | <mailman.186.1308592755.1164.python-list@python.org> |
| In reply to | #7996 |
On 6/20/2011 3:12 AM, Benjamin Kaplan wrote: > On Sun, Jun 19, 2011 at 9:04 PM, John Salerno<johnjsal@gmail.com> wrote: >> On Jun 19, 8:52 pm, Chris Kaynor<ckay...@zindagigames.com> wrote: >> >>> Having a character class (along with possibly player character, non-player character, etc), make sense; however you probably want to make stuff like health, resources, damage, and any other attributes not be handles by any classes or inheritance in order to allow you to make such data-driven (ie, read from a file). Doing so makes the game much more extendable: using classes, you are likely limited to 5 or 'combinations and a few developers (plus, any designers need to know programming). >>> >>> A basic way to determine between using subclasses over a data driven approach is: is there significantly different back-end behavior or merely attribute differences. >> >> Can you give a basic example of how this data-driven approach would >> work? You don't have to provide any code, just a description would be >> helpful. Such as, do I create a data file per character, and then have >> each character instance read/write to that file? Is it good to have so >> many files open at once, or would they only need to be read, closed, >> then opened again at the end to write? >> -- > > I'm pretty sure he means that if the only difference between classes > is configuration (i.e. you aren't actually going to change code > between character classes, just base stats, growth rates, and a list > of available skills or something of that nature), then you should > store the configurations in a config file rather than making a new > class. So rather than having > class WizardCharacter(Character) : > base_health = 50 > ... > class WarriorCharacter(Character) : > base_health=70 > ... > You make a config file > > --- characterclasses.ini --- > [Wizard] > base_health=50 int = 70 > [Warrior] > base_health=70 int = 30 [Gandolf] base_health = 60 int = 100 My point here being that with a data approach, non-programmers can also define named, unique NPCs with custom stats as well as generic classes > Then, when you make a new character, rather than doing a > WizardCharacter() or a WarriorCharacter(), you do a > Character(job='Wizard') and then look up the various defaults in your > config file. Doing it this way makes it trivial to add a new class. If > you want to use an old-fashioned INI file, you can use the > ConfigParser class to read them. If you want to nest attributes (for > instance, a list of sub-items), you'll probably want to go with XML > and ElementTree. I guess you can also use JSON (which uses a syntax > similar to Python's dictionaries) but I've never really tried to make > one of those by hand before so I'm not sure how well it will work out. -- Terry Jan Reedy
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.python
csiph-web