David Ross
David Ross

Reputation: 1167

How to get a reference to an object attribute?

I am looking for a way to alter an objects attribute depending upon a value parsed to a method. For example:

class Character(object):
    def __init__(self, hp, mp, st, ag):
        self.hp = hp
        self.mp = mp
        self.st = st
        self.ag = ag

    def increase_stat(self, stat, value):
        ''' Increase one of the attributes
            (hp, mp, st, ag) by a given value.
        '''
        stat_dict = {'hp': self.hp,
                     'mp': self.mp,
                     'st': self.st,
                     'ag': self.ag}
        stat_attr = stat_dict.get(stat)
        # below should be equivalent to self.hp += value
        stat_attr += value


hero = Character(hp=10, mp=4, st=3, ag=2)
hero.increase_stat(stat='hp', value=2)
# this should increase the hp by 2
print(hero.hp == 12)

I understand this doesn't work as stat_dict.get(stat) returns the value self.hp is pointing to rather than the actual self.hp object.

Current Solution

Instead of having the attributes as values for the dict, I have methods which increase each individual stat.

class Character(object):
    def __init__(self, hp, mp, st, ag):
        self.hp = hp
        self.mp = mp
        self.st = st
        self.ag = ag

    def increase_stat(self, stat, value):
        ''' Increase one of the attributes
            (hp, mp, st, ag) by a given value.
        '''
        stat_method_dict = {'hp': self._increase_hp,
                            'mp': self._increase_mp,
                            'st': self._increase_st,
                            'ag': self._increase_ag}
        alter_stat_method = stat_method_dict.get(stat)
        alter_stat_method(value)

    def _increase_hp(self, value):
        self.hp += value

    def _increase_mp(self, value):
        self.mp += value

    def _increase_st(self, value):
        self.st += value

    def _increase_ag(self, value):
        self.ag = value


hero = Character(hp=10, mp=4, st=3, ag=2)
hero.increase_stat(stat='hp', value=2)
# this should increase the hp by 2
print(hero.hp == 12)

My problem with this is it is repetitive and if I decide to add more attributes/stats to the class then this will only increase the number of repetitive methods. I was wondering if there is a better solution to this than what I have produced above?

SOLUTION

Seems very obvious now. Thanks all.

class Character(object):
    def __init__(self, hp, mp, st, ag):
        self.hp = hp
        self.mp = mp
        self.st = st
        self.ag = ag

    def increase_stat(self, stat, value):
        ''' Increase one of the attributes
            (hp, mp, st, ag) by a given value.
        '''
        current = getattr(self, stat) 
        setattr(self, stat, current+value)

Upvotes: 1

Views: 373

Answers (2)

juanpa.arrivillaga
juanpa.arrivillaga

Reputation: 96287

Your stat-dict really serves no purpose. Use getattr and setattr to dynamically manipulate attributes. So, something like:

def increase_stat(self, stat, value):
    setattr(self, stat, value + getattr(self, stat))

So, e.g.:

In [30]: class Dummy:
    ...:     def __init__(self):
    ...:         self.intelligence = 0
    ...:         self.strength = 10
    ...:         self.hp = 100
    ...:     def increase_stat(self, stat, value):
    ...:         setattr(self, stat, value + getattr(self, stat))
    ...:

In [31]: d = Dummy()

In [32]: d.strength
Out[32]: 10

In [33]: d.increase_stat('strength', 10)

In [34]: d.strength
Out[34]: 20

Perhaps easier to read:

def increase_stat(self, stat, value):
    current = getattr(self, stat)
    setattr(self, stat, current + value)

Note, this is better than manipulating the instance __dict__ directly, which will break with descriptors like property objects. Not to mention objects that don't have a __dict__ e.g. types with __slots__

Upvotes: 2

Ajax1234
Ajax1234

Reputation: 71471

You can use the __setitem__ method for a more concise solution:

class Character(object):
  def __init__(self, *args):
     self.__dict__ = dict(zip(['hp', 'mp', 'st', 'ag'], args))
  def __setitem__(self, name, val):
     self.__dict__[name] += val

hero = Character(10, 4, 3, 2)
hero['hp'] = 2
print(hero.hp)

Output:

12

However, this strategy can also be implemented using a custom method:

class Character(object):
  def __init__(self, *args):
     self.__dict__ = dict(zip(['hp', 'mp', 'st', 'ag'], args))
  def increase_stat(self, stat, value):
     self.__dict__[stat] += value

hero = Character(10, 4, 3, 2)
hero.increase_stat(stat='hp', value=2)

Output:

12

Upvotes: 0

Related Questions