class Character(object):
def __init__(self, name, stats):
self.name = name
self.strength = stats[0]
self.dexterity = stats[1]
self.intelligence = stats[2]
self.luck = stats[3]
Is this a good way to assign the values to the different attributes?
Should 'stats' be a list/tuple (like this), or should I do *stats instead?
I'm trying to think ahead to when I might want to add new attributes,
and I want to make sure this doesn't get crazy with individual
parameters instead of just the one list.
Or maybe there's some way to loop through two lists (the stats and the
attributes) and assign them that way? I was thinking of a nested for
statement but that didn't seem to work.
Sounds like what you should be doing is something like keyword arguments
instead.
class Character(object):
def __init__(self, name, **kwargs):
self.name=name
for key, value in kwargs.items():
setattr(self, key, value)
z=Character('name', strength=10, dexterity=5, intelligence=3, luck=0)
Now you can easily introduce new keyword arguments.
-Larry
How about:
class Character(object):
def __init__(self, name, **kwargs):
self.name = name
self.__dict__.update(kwargs)
c = Character( "Plato", strength=10, luck=12)
print getattr(c, "strength")
print getattr(c, "luck")
10
12
If your program deals with 4-element tuples then although you *could*
use *stats in your calls to pass each element of the tuple as a single
argument, that's not really necessary. A way to write the
initializations you want without using indexing is:
class Character(object):
def __init__(self, name, stats):
self.name = name
self.strength, self.dexterity, \
self.intelligence, self.luck = stats
regards
Steve
--
Steve Holden +44 150 684 7255 +1 800 494 3119
Holden Web LLC/Ltd http://www.holdenweb.com
Skype: holdenweb http://holdenweb.blogspot.com
Recent Ramblings http://del.icio.us/steve.holden
> Let's say I'm making a game and I have this base class:
>
> class Character(object):
>
> def __init__(self, name, stats):
> self.name = name
> self.strength = stats[0]
> self.dexterity = stats[1]
> self.intelligence = stats[2]
> self.luck = stats[3]
>
> Is this a good way to assign the values to the different attributes?
> Should 'stats' be a list/tuple (like this), or should I do *stats instead?
Whenever possible, think about writing self-documenting code:
def __init__(self, name, strength, dexterity, intelligence, luck):
self.name = name
self.strength = strength
# etc.
seems perfectly acceptable to me (if a tad verbose, but that isn't a big
deal -- write once, never touch again).
The problem with function signatures like these:
def __init__(self, name, stats):
def __init__(self, name, *stats):
is that the format of stats is left unstated. Is it (luck, strength,
intelligence) or (strength, intelligence, luck) or (wisdom, charisma,
power, health) or something else? You shouldn't need to read the code
line by line to find out, and relying on documentation risks having the
code and docs get out of sync.
If you expect the stats to be passed as a single tuple, you can still make
it explicit: just wrap the field names within brackets.
def __init__(self, name, (strength, dexterity, intelligence, luck) ):
> I'm trying to think ahead to when I might want to add new attributes,
If this is likely, you could do something like this:
def __init__(self, name, **stats):
self.name = name
self.__dict__.update(stats)
Adding extra attributes is fine, since they will just be ignored, but what
if the caller adds an attribute "itnelligence" (instead of intelligence)?
You're now writing lots of code like this:
def save_intelligence(self, threshold):
"""Roll a saving throw against intelligence"""
try:
return roll(self.intelligence) > threshold
except AttributeError:
# character has no intelligence, so always fails
return False
Yes, you can make that easier with currying, decorators etc. but why not
make sure your characters have the required attributes in the first place?
One way of doing that would be to add default values for the required
attributes in the class, and let instances inherit those defaults from the
class.
> and I want to make sure this doesn't get crazy with individual
> parameters instead of just the one list.
If you've got that many character attributes, I'm guessing that your game
will be a tad hard to play :-)
If you have more than a half-dozen or ten character attributes, you could
consider encapsulating them in some way. E.g. group-related attributes and
pass them as tuples:
power => (constitution, health, anaerobic_strength, aerobic_strength)
intelligence => (IQ, wisdom, dexterity, book_learning, street_wisdom)
charisma => (beauty, chutzpah, attractiveness, persuasiveness)
senses => (vision, hearing, smell, telepathy, empathy, feeling, spacial)
others => (luck, determination, laziness, attention_to_detail)
You could then roll against power, say, by giving a set of weights:
character.roll('power', (0.0, 0.0, 0.9, 0.1))
gives anaerobic strength a weighting of 90% and aerobic 10%.
But again, I think that if your roll-playing game needs to have such
fine-grained attributes, I think it will be too complex to be fun.
> Or maybe there's some way to loop through two lists (the stats and the
> attributes) and assign them that way? I was thinking of a nested for
> statement but that didn't seem to work.
def __init__(self, stats):
names = ['strength', 'dexterity', 'intelligence', 'luck']
for name, stat in zip(names, stats):
setattr(self, name, stat)
--
Steven.
> Let's say I'm making a game and I have this base class:
>
> class Character(object):
>
> def __init__(self, name, stats):
> self.name = name
> self.strength = stats[0]
> self.dexterity = stats[1]
> self.intelligence = stats[2]
> self.luck = stats[3]
>
> Is this a good way to assign the values to the different attributes?
> Should 'stats' be a list/tuple (like this), or should I do *stats
> instead?
A tuple is fine, but assumes that there is a logical *sequence* to
these values. If that's true, it should be no problem to assign them as:
class Character(object):
def __init__(self, name, stats):
self.name = name
(self.strength, self.dexterity,
self.intelligence, self.luck) = stats
>>> foo = Character("Foo", (10, 11, 9, 10))
>>> print foo.name, foo.strength, foo.intelligence
Foo 10 9
In this case, though, I don't see any value in a specific sequence, so
a mapping looks better to me and gives the caller more flexibility in
how to set it up.
class Character(object):
stat_keys = ['strength', 'dexterity', 'intelligence', 'luck']
def __init__(self, name, stats):
self.name = name
self.stats = {}
for (stat_key, stat_value) in [(k, stats[k])
for k in self.stat_keys]:
setattr(self, stat_key, stat_value)
>>> foo = Character("Foo", dict(
... dexterity = 11, luck = 10,
... strength = 10, intelligence = 9,
... ))
>>> print foo.name, foo.strength, foo.intelligence
Foo 10 9
> I'm trying to think ahead to when I might want to add new
> attributes
In which case you almost certainly want a mapping, not a sequence.
> and I want to make sure this doesn't get crazy with individual
> parameters instead of just the one list.
This is one of the many reasons why Python's built-in composite types
are so useful. A group of semantically-related values can be passed as
a single composite value.
--
\ "I hope that after I die, people will say of me: 'That guy sure |
`\ owed me a lot of money.'" -- Jack Handey |
_o__) |
Ben Finney
I would say this is a bad idea because you'll get no error messages if
you mis-spell 'dexterity' for instance.
I'd prefer to see a bit more error checking, something like
class Character(object):
attributes = set(['strength', 'dexterity', 'intelligence', 'luck'])
def __init__(self, name, **kwargs):
self.name=name
for key, value in kwargs.items():
if key not in self.attributes:
raise AttributeError("Bad attribute %s for %s" % (key, self.__class__.__name__))
setattr(self, key, value)
z = Character('name', strength=10, dexterity=5, intelligence=3, luck=0)
>>> Character('name', strength=10, dexterity=5, intelligence=3, luck=0)
<Character object at 0xb7dac72c>
>>> Character('name', strength=10, dextrity=5, intelligence=3, luck=0)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "z.py", line 7, in __init__
raise AttributeError("Bad attribute %s for %s" % (key, self.__class__.__name__))
AttributeError: Bad attribute dextrity for Character
>>>
You can then add to attributes in the subclasses
class MagicCharacter(Character):
attributes = Character.attributes | set(['spells', 'wand'])
>>> MagicCharacter('name', strength=10, dexterity=5, intelligence=3, luck=0, spells=1)
<MagicCharacter object at 0xb7ce86ac>
>>>
If I was writing this, I'd probably just stick to named parameters
unless I had more than 10 parameters. Named parameters are easy to
manage, and you never get confused by their position.
Also pychecker understands named parameters where as if you use a
scheme like the above you'll cause pychecker problems!
--
Nick Craig-Wood <ni...@craig-wood.com> -- http://www.craig-wood.com/nick
> Is this a good way to assign the values to the different attributes?
> Should 'stats' be a list/tuple (like this), or should I do *stats instead?
Thanks guys! The main suggestion seems to be to use setattr(), so I
might give that a try. But I do like Steve's suggestion that it's better
to be explicit about each attribute, instead of just accepting a list of
numbers (but I can't help but feel that for some reason this is better,
because it's more general). We shall see! :)
> But I do like Steve's suggestion that it's better to be explicit
> about each attribute, instead of just accepting a list of numbers
> (but I can't help but feel that for some reason this is better,
> because it's more general).
If you pass a *mapping* of the "I-might-want-to-add-more-in-the-future"
values, then you get both explicit *and* expandable, without an
arbitrary unneeded sequence.
--
\ "He may look like an idiot and talk like an idiot but don't let |
`\ that fool you. He really is an idiot." -- Groucho Marx |
_o__) |
Ben Finney
Do you mean by using the **kwargs parameter? If I do this, doesn't it
mean that *anything* could be added though? Misspelled words and
completely unrelated attributes as well?
Or does this matter as long as you are handling the processing yourself
internally and not allowing users access to the Character class?
> Ben Finney wrote:
>> John Salerno <john...@NOSPAMgmail.com> writes:
>>
>>> But I do like Steve's suggestion that it's better to be explicit
>>> about each attribute, instead of just accepting a list of numbers
>>> (but I can't help but feel that for some reason this is better,
>>> because it's more general).
>>
>> If you pass a *mapping* of the "I-might-want-to-add-more-in-the-future"
>> values, then you get both explicit *and* expandable, without an
>> arbitrary unneeded sequence.
>>
>
> Do you mean by using the **kwargs parameter?
Yes, that's what Ben is talking about.
> If I do this, doesn't it
> mean that *anything* could be added though? Misspelled words and
> completely unrelated attributes as well?
**kwargs flexibility carries a risk. You may consider it worthwhile or not.
> Or does this matter as long as you are handling the processing yourself
> internally and not allowing users access to the Character class?
Only you can decide whether **kwargs' convenience and flexibility
outweighs its risk.
--
Steven.
> Ben Finney wrote:
> > If you pass a *mapping* of the
> > "I-might-want-to-add-more-in-the-future" values, then you get both
> > explicit *and* expandable, without an arbitrary unneeded sequence.
>
> Do you mean by using the **kwargs parameter?
No. I mean what I said in this earlier post on this thread:
<URL:http://groups.google.com/group/comp.lang.python/msg/b413bb1f311e7ee1>
If you have a group of named, semantically-related, unsequenced
values, pass them into the function as a mapping object (a dict
object).
--
\ "I have yet to see any problem, however complicated, which, |
`\ when you looked at it in the right way, did not become still |
_o__) more complicated." -- Paul Anderson |
Ben Finney
> John Salerno <john...@NOSPAMgmail.com> writes:
>
>> Ben Finney wrote:
>> > If you pass a *mapping* of the
>> > "I-might-want-to-add-more-in-the-future" values, then you get both
>> > explicit *and* expandable, without an arbitrary unneeded sequence.
>>
>> Do you mean by using the **kwargs parameter?
>
> No.
Well, that'll teach me to put words in your mouth.
[snip]
> If you have a group of named, semantically-related, unsequenced values,
> pass them into the function as a mapping object (a dict object).
Still, if you are doing this:
mapping_object = {"strength": roll_dice(10),
"intelligence":roll_dice(10),
"dexterity":roll_dice(10)}
my_character = Character(mapping_object)
then there is little benefit to building the dict just for the purposes of
passing it to Character(), never to use it again, not when you can do this:
my_character = Character(strength: roll_dice(10),
intelligence:roll_dice(10), dexterity:roll_dice(10))
If you happen to already have collected your character attributes in a
mapping object for some other reason, then well and good, pass it into the
function. Otherwise, well, I believe the correct container for
character attributes is a Character, not a dict.
--
Steven.
But you will have to adapt this if you want extra or different
characteristics.
Personnally I would prefer something like:
chardict = {}
for char in characteristics:
chardict[char] = roll_dice(10)
my_character = Character(chardict)
This way you only have to keep your characteristics in one place.
> If you happen to already have collected your character attributes in a
> mapping object for some other reason, then well and good, pass it into the
> function. Otherwise, well, I believe the correct container for
> character attributes is a Character, not a dict.
What is wrong with keeping the character attributes in a dict in the
Character?
--
Antoon Pardon
Sure, but only in one place:
# now have charisma
my_character = Character(strength=roll_dice(8),
intelligence=roll_dice(12), dexterity=roll_dice(20),
charisma=roll_dice(6))
If all the char attributes are initialised with the same function, it
may make sense to set them in a loop, as you do below. But if they are all
calculated differently, as above, then you lose the benefit of a loop.
> Personnally I would prefer something like:
>
> chardict = {}
> for char in characteristics:
> chardict[char] = roll_dice(10)
>
> my_character = Character(chardict)
>
> This way you only have to keep your characteristics in one place.
As I do.
Remember, the prerequisite for my suggestion to make sense is that, once
you've created your initial character attributes and stuck them in a dict,
you never use the dict again. Also, I'm assuming the constraint that there
is a manageably small number of character attributes.
>> If you happen to already have collected your character attributes in a
>> mapping object for some other reason, then well and good, pass it into the
>> function. Otherwise, well, I believe the correct container for
>> character attributes is a Character, not a dict.
>
> What is wrong with keeping the character attributes in a dict in the
> Character?
For the same reason we typically say object.attribute rather than
object.__dict__[attribute]
Perhaps you missed the original post, where one of the constraints was
that character attributes in the game were also object attributes. E.g.
class Character(object):
def __init__(self, strength):
self.strength = strength
The question posed was, what is the best way of calling __init__ with
values for those character attributes?
If the character attributes vary at runtime, or there are many of them, or
if they are needed together (rather than individually) in multiple places
apart from Character.__init__, then it makes sense to bundle them up in a
dict and pass the dict around, like Ben and now you are suggesting.
But if the character attributes are not varying, and there are only a few,
and they only get used collectively for Character.__init__, then I don't
believe there is any advantage to putting them in a dict to be used once
and then tossed away.
To give an analogy, if you are writing a coordinate class, you would
likely do something like this:
class Coord(object):
def __init__(self, x, y):
self.x = x
self.y = y
If you had a lot of dimensions, you'd change your entire model:
class Coord(object):
def __init__(self, alist):
self.coordinates = alist
But it is unlikely that a tactic like this would be worth the extra work:
class Coord(object):
def __init__(self, mapping_object):
# check that mapping_object has the right keys
expected = ['x', 'y']
for key in expected:
if key not in mapping_object.keys(): raise KeyError
# do something with mapping_object
self.__dict__.update(mapping_object)
mapping = {'x': some_value, 'y': some_value}
point = Coord(mapping)
del mapping # never use it again after construction
--
Steven.
> If you happen to already have collected your character attributes in a
> mapping object for some other reason, then well and good, pass it into the
> function. Otherwise, well, I believe the correct container for
> character attributes is a Character, not a dict.
>
Are your sure? The OP had subclasses like Fighter, Mage etc.
So it seems that you have to do something like this for
every such subclass. Or am I missing something.
> # now have charisma
> my_character = Character(strength=roll_dice(8),
> intelligence=roll_dice(12), dexterity=roll_dice(20),
> charisma=roll_dice(6))
>
> If all the char attributes are initialised with the same function, it
> may make sense to set them in a loop, as you do below. But if they are all
> calculated differently, as above, then you lose the benefit of a loop.
Not necesarily. You could have a table as follows:
characteristics = [("strength", partial(roll_dice, 8)),
("inteligence" , partial(roll_dice, 12),
...
chardict={}
for char, roller in characteristics:
chardict[char] = roller()
>> Personnally I would prefer something like:
>>
>> chardict = {}
>> for char in characteristics:
>> chardict[char] = roll_dice(10)
>>
>> my_character = Character(chardict)
>>
>> This way you only have to keep your characteristics in one place.
>
> As I do.
>
> Remember, the prerequisite for my suggestion to make sense is that, once
> you've created your initial character attributes and stuck them in a dict,
> you never use the dict again. Also, I'm assuming the constraint that there
> is a manageably small number of character attributes.
That depends on how the rest of the game is set up. Suppose the
character is wandering through a maze. Now in the maze are "tests"
which require the character to roll under a certain characteristic
with a certain bonus/malus. So such a test object value may be
equivallent to ("strength" , -2) I think such a setup will be
easier to implement if you keep the characteristics as a dictionary
within a character instance.
Now whether such a scheme would suit the OP is up to him to decide.
>>> If you happen to already have collected your character attributes in a
>>> mapping object for some other reason, then well and good, pass it into the
>>> function. Otherwise, well, I believe the correct container for
>>> character attributes is a Character, not a dict.
>>
>> What is wrong with keeping the character attributes in a dict in the
>> Character?
>
> For the same reason we typically say object.attribute rather than
> object.__dict__[attribute]
But we also typically use:
object.some_dict[some_characteristic]
rather than
object.__dict__[some_characterictic]
where some_charaterictic contains the name of a characteristic.
> Perhaps you missed the original post, where one of the constraints was
> that character attributes in the game were also object attributes. E.g.
>
> class Character(object):
> def __init__(self, strength):
> self.strength = strength
>
> The question posed was, what is the best way of calling __init__ with
> values for those character attributes?
Well if that is what he wants.
> If the character attributes vary at runtime, or there are many of them, or
> if they are needed together (rather than individually) in multiple places
> apart from Character.__init__, then it makes sense to bundle them up in a
> dict and pass the dict around, like Ben and now you are suggesting.
It is not only that they can vary ay runtime. There is also the
possibility that you don't know in advance which one you will need
because that is decided by the environment. I think that would be
an argument in favor of a dict too.
> But if the character attributes are not varying, and there are only a few,
> and they only get used collectively for Character.__init__, then I don't
> believe there is any advantage to putting them in a dict to be used once
> and then tossed away.
I guess it depends on how he will need those characteristics later on.
> To give an analogy, if you are writing a coordinate class, you would
> likely do something like this:
>
> class Coord(object):
> def __init__(self, x, y):
> self.x = x
> self.y = y
But what if the application often needed one value from such an
object. So lots of time you would be given a coord plus an
indication of what value was wanted by giving a name, so that
you ended up doing a lot of getattr(coord, name) calls.
Because whether you needed the x or y coordinate is decided
by the data and not by the program structure.
May be the O.P. should decide first on how he is going to use
these characteristics in the rest of his game before deciding
what is the best approach.
--
Antoon Pardon