assigning values in __init__

Discussion in 'Python' started by John Salerno, Nov 6, 2006.

  1. John Salerno

    John Salerno Guest

    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?

    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.
     
    John Salerno, Nov 6, 2006
    #1
    1. Advertising

  2. John Salerno

    Larry Bates Guest

    John Salerno wrote:
    > 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?
    >
    > 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
     
    Larry Bates, Nov 6, 2006
    #2
    1. Advertising

  3. John Salerno wrote:
    > 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?
    >


    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
     
    Gerard Flanagan, Nov 6, 2006
    #3
  4. John Salerno

    Steve Holden Guest

    John Salerno wrote:
    > 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?
    >
    > 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.


    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
     
    Steve Holden, Nov 7, 2006
    #4
  5. On Mon, 06 Nov 2006 16:57:58 -0500, John Salerno wrote:

    > 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.
     
    Steven D'Aprano, Nov 7, 2006
    #5
  6. John Salerno

    Ben Finney Guest

    John Salerno <> writes:

    > 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
     
    Ben Finney, Nov 7, 2006
    #6
  7. Larry Bates <> wrote:
    > John Salerno wrote:
    > > 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?
    > >
    > > 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)


    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 <> -- http://www.craig-wood.com/nick
     
    Nick Craig-Wood, Nov 7, 2006
    #7
  8. John Salerno

    John Salerno Guest

    John Salerno wrote:

    > 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! :)
     
    John Salerno, Nov 7, 2006
    #8
  9. John Salerno

    Ben Finney Guest

    John Salerno <> 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.

    --
    \ "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
     
    Ben Finney, Nov 7, 2006
    #9
  10. John Salerno

    John Salerno Guest

    Ben Finney wrote:
    > John Salerno <> 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? 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?
     
    John Salerno, Nov 8, 2006
    #10
  11. On Wed, 08 Nov 2006 10:55:57 -0500, John Salerno wrote:

    > Ben Finney wrote:
    >> John Salerno <> 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.
     
    Steven D'Aprano, Nov 8, 2006
    #11
  12. John Salerno

    Ben Finney Guest

    John Salerno <> 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. 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
     
    Ben Finney, Nov 9, 2006
    #12
  13. On Thu, 09 Nov 2006 12:27:12 +1100, Ben Finney wrote:

    > John Salerno <> 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.
     
    Steven D'Aprano, Nov 9, 2006
    #13
  14. On 2006-11-09, Steven D'Aprano <> wrote:
    > On Thu, 09 Nov 2006 12:27:12 +1100, Ben Finney wrote:
    >
    >> John Salerno <> 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))


    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
     
    Antoon Pardon, Nov 9, 2006
    #14
  15. On Thu, 09 Nov 2006 10:36:07 +0000, Antoon Pardon wrote:

    > On 2006-11-09, Steven D'Aprano <> wrote:
    >> On Thu, 09 Nov 2006 12:27:12 +1100, Ben Finney wrote:
    >>
    >>> John Salerno <> 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))

    >
    > But you will have to adapt this if you want extra or different
    > characteristics.


    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.
     
    Steven D'Aprano, Nov 9, 2006
    #15
  16. John Salerno

    Steve Holden Guest

    Steven D'Aprano wrote:
    > On Thu, 09 Nov 2006 12:27:12 +1100, Ben Finney wrote:
    >
    >> John Salerno <> 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))
    >

    Except, of course, that you *can't* do that: I think you meant to use
    equals signs instead of colons?

    > 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.
    >

    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
     
    Steve Holden, Nov 9, 2006
    #16
  17. On 2006-11-09, Steven D'Aprano <> wrote:
    > On Thu, 09 Nov 2006 10:36:07 +0000, Antoon Pardon wrote:
    >
    >> On 2006-11-09, Steven D'Aprano <> wrote:
    >>> On Thu, 09 Nov 2006 12:27:12 +1100, Ben Finney wrote:
    >>>
    >>>> John Salerno <> 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))

    >>
    >> But you will have to adapt this if you want extra or different
    >> characteristics.

    >
    > Sure, but only in one place:


    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
     
    Antoon Pardon, Nov 9, 2006
    #17
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Steven Bethard
    Replies:
    2
    Views:
    480
    Steven Bethard
    Feb 16, 2005
  2. Kent Johnson
    Replies:
    7
    Views:
    941
    Jan Niklas Fingerle
    Feb 12, 2006
  3. Gnarlodious

    __init__ with multiple list values

    Gnarlodious, Oct 30, 2011, in forum: Python
    Replies:
    4
    Views:
    253
    Jean-Michel Pichavant
    Nov 2, 2011
  4. weston
    Replies:
    1
    Views:
    277
    Richard Cornford
    Sep 22, 2006
  5. Ramchandra Apte
    Replies:
    17
    Views:
    364
    Manuel Pégourié-Gonnard
    Sep 30, 2012
Loading...

Share This Page