Generating Variable Names on Fly in Python

How can you dynamically create variables?

Unless there is an overwhelming need to create a mess of variable names, I would just use a dictionary, where you can dynamically create the key names and associate a value to each.

a = {}
k = 0
while k < 10:
# dynamically create key
key = ...
# calculate value
value = ...
a[key] = value
k += 1

There are also some interesting data structures in the collections module that might be applicable.

Create variables with specific type on the fly in Python

Well, without getting into the details of your nesting, you could attach a variable type to the name by using a tuple.

I've done this on 2 of your variable names : ('_idx_brg',str), ('stn','int')

Rather than using zip, you'll need to hook that back up to your nested tuples and you'll also need to add error handling in case the string value from the file doesn't fit the expected variable type.

import builtins
import pdb

def set_attr(tgt, names, values):

try:
for name, value in zip(names, values):
cls_ = None
if isinstance(name, str):
setattr(tgt, name, float(value))
elif isinstance(name, tuple):
name, cls_ = name
if callable(cls_):
setattr(tgt, name, cls_(value))
elif isinstance(cls_, str):
cls_ = globals().get(cls_) or getattr(builtins, cls_)
setattr(tgt, name, cls_(value))
else:
raise ValueError("variable types have to be a string or callable like `int`,`float`, etc")
except (ValueError,TypeError,AttributeError) as e:
print(f" somethings wrong:\n{dict(exception=e, name=name, cls_=cls_, value=value)}")
#raise

#pragma: no cover pylint: disable=unused-variable
except (Exception,) as e:
if 1:
pdb.set_trace()
raise

class Foo:
pass

variable_names = ('_idx_brg', 'stn', 'stn_rel', '_n_lines', '_brg_type')
values = (1.0, 1, 1.2, 1.3, 1.4, 1.5)

foo = Foo()

print("\n\nsetting for foo")
set_attr(foo, variable_names, values)

print("\n\nfoo:", vars(foo))

variable_names2 = (('_idx_brg',str), ('stn','int'), 'stn_rel', '_n_lines', ('_brg_type','xxx'))

bar = Foo()

print("\n\nsetting for bar:")
set_attr(bar, variable_names2, values)

print("\n\nbar:", vars(bar))

output:


setting for foo

foo: {'_idx_brg': 1.0, 'stn': 1.0, 'stn_rel': 1.2, '_n_lines': 1.3, '_brg_type': 1.4}

setting for bar:
somethings wrong:
{'exception': AttributeError("module 'builtins' has no attribute 'xxx'"), 'name': '_brg_type', 'cls_': 'xxx', 'value': 1.4}

bar: {'_idx_brg': '1.0', 'stn': 1, 'stn_rel': 1.2, '_n_lines': 1.3}
br>

You could even build your own classes.

class Myclass:
def __init__(self, value):
self.value = value

#part of your name/type tuples...
(('somevar', Myclass), ('_idx_brg',str)...)

edit re. yaml:

I am not testing this so you may have to adjust a bit, esp around the exact yaml to get a dict with a nested varnames dict in it.

---
varnames:
_idx_brg: str
stn : int
from yaml import safe_load as yload
with open("myconfig.yaml") as fi:
config = yload(li)

mapping = {}

#the yaml is all strings right now
# map it to actual types/classes
for name, type_ in config["varnames"].items():
cls_ = globals().get(type_) or getattr(builtins, type_)
mapping[name] = cls_

#using it
for name, value in zip(names, values):

#fall back to `float` if there is no special-case for this varname
cls_ = mapping.get(name, float)
setattr(tgt, name, cls_(value))

Now, this does rely on all instances of a given variable name having the same type no matter where in the data hierarchy, but that's just best practices.

The other thing is that, if I have one area that looks a bit fishy/brittle to me, it is your complex nesting with tuples of values and names that somehow need to be always in synch. Much more so than your basic requirement to load text data (whose format is not under your control) but then format it different ways. I'd work at getting your names to flow more naturally with the data, somehow. Maybe try to identify incoming data by record types and then assign a mapping class to it? Same thing as what you're doing, really, just not relying on complex nesting.

Or maybe, going from your remark about row, column, you could put all that into the yaml config file as well, load that into a mapping data structure and explicitly use indices rather than nested loops? Might make your code a lot simpler to reason about and adjust for data changes.

There are also interesting things in the Python data parsing space like Pydantic. Might or might not be helpful.

Run a loop to generate variable names in Python

Use the inbuilt glob package

from glob import glob

fullpath = f'C:\Users\siddhn\Desktop\phone[1-6].csv'
dfs = [pd.read_csv(file) for file in glob(fullpath)]

print(dfs[0])

Dynamically create variable names?

you can use dictionary like this: examples

>>> a = ['hello', 'banana', 'apple']
>>> my_dict = {}
>>> for x in range(len(a)):
... my_dict[x] = a[x]
...
>>> my_dict
{0: 'hello', 1: 'banana', 2: 'apple'}

>>> my_dict[1]
'banana'

Python. How to dynamically create variables based on number of lines in a file?

It is almost always a bad idea to create variable names based on some programmatic value. Instead use a native data structure. In this case it sounds like a list is what you need.

Here is a way to loop through a file and collect pairs of lines into a list of lists.

var = []
last_line = None
for line in open('data.txt', 'rU'):
if last_line:
var.append([last_line, line.strip()])
last_line = None
else:
last_line = line.strip()
if last_line:
var.append([last_line])
print(var)

Results:

[['line1', 'line2'], ['line3', 'line4'], ['line5', 'line6']]

how to assign a python variable whose name is generated on-the-fly?

you could do

globals()['yourvariables'] = variable

This adds the variable to the global namespace. I am not going to comment on whether it is a good idea or a bad one.

how to make variable name dynamic

The proper way to do it would be to use a dictionary rather than declaring variables :

s = open("Lines_only.txt", "r")
j = 1
v_dict = {}
for i in s:
line = i.strip()
v_dict[f"v{j}"] = float(line)
print(v_dict[f"v{j}"] )
j +=1
s.close()

#Then here access to the variables using v_dict["v1"], v_dict["v2"], ...

However, if what you want is really to declare a variable this is possible too (but you should still use the first option if possible)

s = open("Lines_only.txt", "r")
j = 1
v_dict = {}
for i in s:
line = i.strip()
globals()[f"v{j}"] = float(line)
print(globals()[f"v{j}"])
j +=1
s.close()

#Then here access to the variables using v1,v2, ...
sum_result = v1+v2


Related Topics



Leave a reply



Submit