assigning values in __init__

Nick Craig-Wood nick at craig-wood.com
Tue Nov 7 04:30:05 EST 2006


Larry Bates <larry.bates at websafe.com> 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 <nick at craig-wood.com> -- http://www.craig-wood.com/nick



More information about the Python-list mailing list