Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to increase an object attribute by a variable amount

Tags:

python

oop

class

I have a class in python for a figure with attributes name, health, strength, stealth, agility, weapons and money. There is a shop in the game I'm making to increase the value of any of the integer properties with a specific item. Each integer property can be increased by one of two different items with a different cost and buff strength. The issue I'm having is actually incrementing the attribute by the amount and saving the object.

Here's the code for the object:

class Figure:
    def __init__(self, stats):
        #create figure object
        self.name = stats[0]
        self.health = int(stats[1])
        self.strength = int(stats[2])
        self.stealth = int(stats[3])
        self.agility = int(stats[4])
        self.weapons = int(stats[5])
        self.money = int(stats[6])

    def show_person(self):
        #show object attributes
        print("\n\n{}\n\nHealth: {}\nStrength: {}\nStealth: {}\nCunning: {}\nWeapons: {}\nMoney: £{}".format(self.name.title(),self.health,self.strength,self.stealth,self.cunning,self.weapons, self.money))

    def set_attr(self,attr,buff):
        #increase character attribute by a variable amount
        eval("self.{} = self.{} + {}".format(attr,attr,buff))

I might put friend.set_attr("stealth",10) to increase friend's value of stealth by 10 where friend is a variable that contains one of these Figure objects but this error is thrown:

File Computer Science\python\oop game.py", line 21, in set_attr
  exec(eval("self.{} = self.{} + {}".format(attr,attr,buff)))
File "<string>", line 1
  self.agility = self.agility + 4
                 ^
SyntaxError: invalid syntax

And I can't work out why.

like image 442
Adam B Avatar asked Oct 08 '18 10:10

Adam B


3 Answers

Assignment is a statement and cannot be used inside an eval, which accepts only expressions. You should use exec instead:

exec("self.{} = self.{} + {}".format(attr,attr,buff))

But instead of exec, it's better to use the setattr function:

setattr(self, attr, getattr(self, attr) + buff)
like image 136
blhsing Avatar answered Sep 27 '22 19:09

blhsing


Don't use exec and eval. Use getattr and setattr:

class Foo:
    def __init__(self):
        self.x = 0

    def set_attr(self, attr, buff):
        new_val = getattr(self, attr) + buff
        setattr(self, attr, new_val)

foo = Foo()
foo.set_attr('x', 10)
print(foo.x)
# 10
foo.set_attr('x', 11)
print(foo.x)
# 21

Alternatively it is possible to use vars to modify the attribute directly (which personally I tend to like less):

class Foo:
    def __init__(self):
        self.x = 0

    def set_attr(self, attr, buff):
        vars(self)[attr] += buff

foo = Foo()
foo.set_attr('x', 10)
print(foo.x)
# 10
foo.set_attr('x', 11)
print(foo.x)
# 21
like image 37
DeepSpace Avatar answered Sep 27 '22 19:09

DeepSpace


Just to be clear: you know you could just type

a.foo += 2

If yes, but you need the other method:

Python already has internal functions that does exactly what you're trying to achieve. The methods are called setattr and getattr. Read more about them here. For now, here's how you can use them:

class A:
   b = 3

a = A()

setattr(a, 'b', 5)
print(a.b) # 5
print(getattr(a, 'b')) # 5

setattr(a, 'b', getattr(a, 'b') + 5)
print(a.b) # 10

So you could implement a method that increments an attribute like this:

class A:
   def incr_attr(self, attr_name, amount):
      setattr(self, attr_name, getattr(self, attr_name) + amount)

Or, even more convenient:

def incr_attrs(self, **attr_map):
    for attr_name, amount in attr_map.items():
         setattr(self, attr_name, getattr(self, attr_name) + amount)

So you can type

A.incr_attr(stealth=3, money=10)
like image 40
Nearoo Avatar answered Sep 27 '22 17:09

Nearoo