Dynamic variable name

Suppose i have to set

q_variable = 0
a_variable = 0

def stats(choice):
     choice + '_variable' += 1

stats('q')

Here after adding the choice + ‘_variable’ will become string.
If i want to change the q_variable or the a_variable depending on the choice we provide to the function.
How do we do that?

1 Like

Use a dictionary;

variables = {"q": 0, "a": 0}

def stats(choice):
    variables[choice] += 1

stats("q")
2 Likes

maybe not use vars as the name of the dictionary, as it is a builtin on its own.

2 Likes

Good point, changed it to variables.

As suggested, use dictionary or any other suitable data structure. Because if you create variables dynamically you probably want to access them dynamically as well. Now you have two problems.

You can do what you want but you must consider very-very seriously whether is it what you actually need. As it happens, globals are also kept in dictionary so you can always make your life exciting:

>>> a_variable = 1
>>> q_variable = 2
>>> def stats(choice):
...     globals()[f'{choice}_variable'] +=1
...
>>> stats('a')
>>> a_variable
2
>>> stats('a')
>>> a_variable
3
>>> stats('q')
>>> q_variable
3

I’m somewhat curious: why would you want to do this? What is your use case?

It seems to me that, if scaled up, this would have a detrimental effect on the readability of the code script as well as a complicated way to code. If things seem to be complicated in Python, there’s usually a better (or a less complicated) way to write the code.

1 Like

how about something more meaningful, such as choices.

I mean, sure, in an actual application you should never name a variable variables. This is just an example, and without knowing what the purpose of the variable is it’s not possible to select a good name.

I can provide some use case.

Lets assume you are testing Windows Calculator as a desktop app.

You generate test data and need to verify that sum or prod works.

Every time calculation is done result is displayed in a window that is an object.

Object name is

Aliases.Microsoft_WindowsCalculator.Calculator.NavView.LandmarkTarget.Display_is_XXXX.TextContainer.NormalOutput

Where XXXX is the result of your calculation. E.g. 2 or 5 or 2023.

And you need to access this object to verify

When you try to get properties of a “parent” object

Aliases.Microsoft_WindowsCalculator.Calculator.NavView.LandmarkTarget

with a dir() function you get this

['_IDispatchWrapper__cookie', '_IDispatchWrapper__hasOwnAttr', '_IDispatchWrapper__raiseBadParamCount', '_IDispatchWrapper__raiseObjectIsNotCallable', '_IDispatchWrapper__raiseObjectIsNotConvertableToType', '_IDispatchWrapper__raiseObjectIsNotIndexable', '_IDispatchWrapper__safeEval', '__bool__', '__call__', '__callmethod__', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__float__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__getitem__', '__getprop__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__int__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__setprop__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']

I am not an expert in Python so if someone knows how to you solve this problem with a dictionary (of reasonable size) or some other way please share.

When you need this highly dynamic sort of program, you should reach out for the hasattr, getattr, and setattr functions. Note that they only work in a class:

class Foo:
    x_var = 10
    y_var = 25

foo = Foo()

# equivalent to `foo.x_var = foo.y_var + 5`
setattr(foo, "x_var", getattr(foo, "y_var") + 5)

print(foo.x_var)  # 30

They are extremely handy when necessary, but somewhat unergonomic to work with

If you have an object that support vars(), like a class or an instance, you can use vars() to access variables with a dynamic name, like so:

class Foo:
  def __init__(self):
    self.x_var = 10
    self.y_var = 25

  def incr(self,varpref):
    vars(self)[f"{varpref}_var"] += 1

foo = Foo()

for i in range(10):
  foo.incr("x")

print(foo.x_var)
>>> 20

Setattr overwrites the atribute, but the vars() function or getattr support methods on the (class) properties:

class Logger:
  def __init__(self):
    self.x_var = []
    self.y_var = []

  def msg(self,varpref,logmsg):
    vars(self)[f"{varpref}_var"].append(logmsg)

  def altmsg(self,varpref,logmsg):
    getattr(self,f"{varpref}_var").append(logmsg)

foo = Logger()

for i in range(10):
  foo.msg("y",f"logmessage {i}")

print(foo.x_var)  
>>> []
print(foo.y_var)  
>>> ['logmessage 0', 'logmessage 1', 'logmessage 2', 'logmessage 3', 'logmessage 4', 'logmessage 5', 'logmessage 6', 'logmessage 7', 'logmessage 8', 'logmessage 9']