2

As part of a bigger project in python3, I need a method in the spirit of to the following (not working) code:

class Qclass:
     def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

def increment(Q, var):
    eval('Q.{} = Q.{} + 100'.format(var,var))

Q = Qclass(1,2,3)
increment(Q,'a')
print(Q.a)
>>> 101

I need this because I don't have prior knowledge on which attribute of Q has to be incremented. A solution would be to declare specific functions for each attribute and use some selector, but I'd prefer to avoid this if possible.

Is there a tidy way to do this ?

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
Aderam
  • 65
  • 5

3 Answers3

3

It is a very bad idea to use eval(..) (in almost any context). You can use getattr(..) and setattr(..) which are more secure:

def increment(Q, var):
    setattr(Q,var,getattr(Q,var)+100)

getattr(obj,name) and setattr(obj,name,val) get and set the attribute of an object. So here you can get/set an attribute if you do not know the name in advance.

So the resulting code is:

class Qclass:
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

def increment(Q, var):
    setattr(Q,var,getattr(Q,var)+100)

Q = Qclass(1,2,3)
increment(Q,'a')
print(Q.a)
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • arrr, yeah! I have totally forgotten about setattr() getattr() . thanks. Although did you edit my question to put the def increment() in Qclass ? I meant it to be at root. – Aderam Feb 09 '17 at 11:49
  • @Aderam: ah that was not the case? There was definitely something wrong with the indentation because `__init__` was in the same indent as `class`... – Willem Van Onsem Feb 09 '17 at 11:50
  • oups, sorry for the typo. Eveything is now exactlly as it should be :) – Aderam Feb 09 '17 at 12:09
0

A combination of setattr and getattr would do:

def increment(Q, var):
    setattr(Q, var, getattr(Q, var) + 100)

yielding 101 for Q.a after your snippet executes.

getattr just grabs an attribute from an object by a given name (and allows for a default to be used if the attribute doesn't exist); similar to Q.<val_name>.

setattr performs the assignment, setting the attribute with the given name on the object.

Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
0

You can use the build-in function vars which returns a dictionary of variables declared in given scope. It's usage looks like this:

Q = Qclass(1,2,3)
try:
    vars(Q)['a'] += 1
except KeyError:
    Q.a = 1
Dunno
  • 3,632
  • 3
  • 28
  • 43
  • Ingenious approach, didn't know about vars(). For the record I have benchmarked this solution against setattr(Q, var, getattr(Q, var) + 1). Seems that they are very close to equal in terms of computational time. Thanks – Aderam Feb 09 '17 at 12:13
  • This approach only works on classes that have a `__dict__`, for classes without (e.g. using `__slots__`) this doesn't work. It's the equivalent of using `Q.__dict__['a'] += 1` – mata Feb 09 '17 at 12:16