So I'm trying to dynamically set properties to a class with different names where each property makes a unique call to my database. However, when I access the property objects, all of them return the same result despite looking up on different columns with completely different values each. Here is the code:
class Configs:
def __init__(self, guild_id):
self.guild_id = guild_id
for x in configs:
def fget(self):
return cur.execute(f'SELECT {x.name} FROM configs WHERE guild_id = ?', (self.guild_id,)).fetchone()[0], x.name
def fset(self, value):
cur.execute(f'UPDATE configs SET {x.name} = ? WHERE guild_id = ?', (value, self.guild_id))
con.commit()
setattr(Configs, x.name, property(fget, fset))
The configs variable is a list of objects where each object has a name
attribute which points to a string. The result is always the one that the last element of the configs array would produce, I suspect this is happening because x.name
is used to make the calls and once the for loop is done, x remains as the last element of the array.
You are under the false impression that defining a function binds the function to the variable values at time of defining. This sounds complex, sorry. I'll try to explain.
You are defining functions in a loop (fget
, fset
). In the functions you use a variable of the loop (x
). This will work, but not in the way you expect it to work. All functions will be exactly alike, always accessing the value of a global variable x
at the time of their calling. The value at the time of defining will not be taken into consideration.
See for example this:
a = []
for i in range(3):
def f(): return i
a.append(f)
a[0]() # will return 2
del i
a[0]() # will raise a NameError because there is no i anymore
To solve your issue you need to pass the value at time of defining into the functions:
def fget(self, x=x):
return cur.execute(f'SELECT {x.name} FROM configs WHERE guild_id = ?', (self.guild_id,)).fetchone()[0], x.name
def fset(self, value, x=x):
cur.execute(f'UPDATE configs SET {x.name} = ? WHERE guild_id = ?', (value, self.guild_id))
con.commit()