Python: change class instance name - create class instance through function [duplicate] - python

This question already has answers here:
How can you dynamically create variables? [duplicate]
(8 answers)
Closed 4 years ago.
I have a class for which I want to create instances through a function, but I also want to be able to name the instances with the value of a Tkinter.Entry widget.
The simplified version of that I am trying to achieve is the following:
class vtdiagram():
IO=0.0
IC=0.0
EO=0.0
EC=0.0
IGA=0.0
def printvtvalues(self):
print self.IO
print self.IC
print self.EO
print self.EC
print self.IGA
def createvtinstance():
global Nametemp
Nametemp=vtdiagram()
If I run this code, then I can call Nametemp.printvtvalues() and get all values printed, so it works fine.
I am now trying to change the name of the instance Nametemp to the string that is on the Tkinter entry widget. Basically, if engine1 is written on the entry box when I createvtinstance(), I would like to then call the instance by:
engine1.printvtvalues()
and get the values.
I imagine the function should look something like this:
def createvtinstance():
global Nametemp
Nametemp=vtdiagram()
Nametemp._command_to_change_the_name_=stringinentrybox.get()
Do you guys have know of a command that can do such a thing?
Or is there a way that I could achieve the same effect, maybe using a dictionary?
***edit: The reason I need to name the variables is for the following (in plain English): I am creating an 'engine simulator'.
The idea is that the user will enter engine parameters -plus its name- in a GUI and this is the vtdiagram class.
The reason for using a class is that I have the characteristics of 'engine1, engine2...' saved as an instance of the class but I also need to have functions attached to it. This is because I want to generate graphs and diagrams of saved engines but only when called. So I can compare engine1 and engine2, but then get 'forget' engine2 from the GUI to compare 1 and 3.
Please keep in mind I am quite new to python :) ***
Many thanks!
Juan

I wouldn't recommend changing the name of a variable based on user input.
You could "achieve the same effect" like this:
Objects=[]
Names=[]
def createvtinstance(Object=4,Name="engine1"):
global Nametemp
global Objects
global Names
Nametemp=Object # I'll just use an int to demonstrate.
Objects+=[Nametemp]
Names+=[Name]
def Use(Name="engine1"):print(Objects[Names.index(Name)]) # Or: Objects[Names.index(Name)].SomeFunction()
If you REALLY want to alter the name of a variable based on user input, then you could do it like this:
def createvtinstance(Name="engine1"):
if (not Name[0]in"qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM") or False in(i in"1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"for i in Name) or Name in("tkinter","createvtinstance","Name","vtdiagram",):return "Invalid name." # This should make the code more "robust".
try:exec("global "+Name+"\n"+Name+"=vtdiagram()")
except SyntaxError:return "Invalid name."
Or this:
def createvtinstance(Name="engine1"):
if (not Name[0]in"qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM") or False in(i in"1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"for i in Name) or Name in("tkinter","createvtinstance","Name","vtdiagram",):raise NameError("The name "+Name+" does not comply to validation rules.") # This should make the code more "robust".
try:exec("global "+Name+"\n"+Name+"=vtdiagram()")
except SyntaxError:raise NameError(Name+" is a reserved keyword.")
The top example shows how you would use a list to find an object in another list; using a string. This is what I'd probably do in this situation, however a dictionary could be better.
The bottom examples show how you would actually name a variable based on user input. This is NOT RECOMMENDED. Everyone seems to agree that using exec is counterproductive, and should be avoided. Python can't compile code in exec statements until execution, and won't be able to colour code your code.
People have been suggesting the use of python dictionaries, so I decided to research them. Dictionaries (dict) seem to be a data type similar to lists, except they can be indexed using strings (or other "immutable" data types). Here is a version of my first example that uses a dictionary instead of lists:
Objects={}
def createvtinstance(Object=4,Name="engine1"):
global Objects
Objects[Name]=Object
def Use(Name="engine1"):print(Objects[Name]) # Or: Objects[Name].SomeFunction()
Python seems to have a built in dictionary called globals, which stores all your variables, so you could probably do:
def createvtinstance(Object=4,Name="engine1"):
globals()[Name]=Object # Or globals()[Name]=vtdiagram()
However, this will allow the user to break your program, if they use a name like createvtinstance or tkinter.

Related

How to use a variable in another function without making it global?

(Py file) So, I am trying to make two commands for a discord bot and in 1 command it makes 3 variables. but I need to use those in another command. it's a Pokémon bot and in the first command it makes 3 random variables
code1 part2 of code1
and so, I need those starter variables in another command called pick_(pokemon name)
await ctx.channel.send (f'Your Starters are... {starterchoice1}, {starterchoice2} and {starterchoice3}. type .pick_The pokemons name to pick')
and I need the variables in this code, so it knows what starters he has to pick from.
I have tried storing the variable value in a JSON file, but I knew there has to be an easier and better way.
If you return the values from the first function directly into the second, it would look similar to this
def pick(poke_list):
# Do things
return starterchoice1, starterchoice2, starterchoice3
def send(starterchoice1, starterchoice2, starterchoice3):
print(f'Your Starters are... {starterchoice1}, {starterchoice2} and {starterchoice3}. type .pick_The pokemons name to pick')
return value
pick1, pick2, pick3 = pick(poke_list)
value = send(pick1, pick2, pick3)
If the two commands are in the same class as where you're instanciating those variables you can use self to refer to the variable.
Why do I need to use "self" to reference the class variable in a class method?
So if you had
Class PokemonSelection():
starterchoice1 = "Squirtle"
starterchoice2 = "Treecko"
starterchoice3 = "Rowlet"
def print_start_choices(self):
print(f'Your Starters are... {self.starterchoice1}, {self.starterchoice2} and {self.starterchoice3}')

How to dynamically return Object attributes in python, including attributes of objects that are attributes

I am trying to write a testing program for a python program that takes data, does calculations on it, then puts the output in a class instance object. This object contains several other objects, each with their own attributes. I'm trying to access all the attributes and sub-attributes dynamically with a one size fits all solution, corresponding to elements in a dictionary I wrote to cycle through and get all those attributes for printing onto a test output file.
Edit: this may not be clear from the above but I have a list of the attributes I want, so using something to actually get those attributes is not a problem, although I'm aware python has methods that accomplish this. What I need to do is to be able to get all of those attributes with the same function call, regardless of whether they are top level object attributes or attributes of object attributes.
Python is having some trouble with this - first I tried doing something like this:
for string in attr_dictionary:
...
outputFile.print(outputclass.string)
...
But Python did not like this, and returned an AttributeError
After checking SE, I learned that this is a supposed solution:
for string in attr_dictionary:
...
outputFile.print(getattr(outputclass, string))
...
The only problem is - I want to dynamically access the attributes of objects that are attributes of outputclass. So ideally it would be something like outputclass.objectAttribute.attribute, but this does not work in python. When I use getattr(outputclass, objectAttribute.string), python returns an AttributeError
Any good solution here?
One thing I have thought of trying is creating methods to return those sub-attributes, something like:
class outputObject:
...
def attributeIWant(self,...):
return self.subObject.attributeIWant
...
Even then, it seems like getattr() will return an error because attributeIWant() is supposed to be a function call, it's not actually an attribute. I'm not certain that this is even within the capabilities of Python to make this happen.
Thank you in advance for reading and/or responding, if anyone is familiar with a way to do this it would save me a bunch of refactoring or additional code.
edit: Additional Clarification
The class for example is outputData, and inside that class you could have and instance of the class furtherData, which has the attribute dataIWant:
class outputData:
example: furtherData
example = furtherData()
example.dataIWant = someData
...
with the python getattr I can't access both attributes directly in outputData and attributes of example unless I use separate calls, the attribute of example needs two calls to getattr.
Edit2: I have found a solution I think works for this, see below
I was able to figure this out - I just wrote a quick function that splits the attribute string (for example outputObj.subObj.propertyIWant) then proceeds down the resultant array, calling getattr on each subobject until it reaches the end of the array and returns the actual attribute.
Code:
def obtainAttribute(sample, attributeString: str):
baseObj = sample
attrArray = attributeString.split(".")
for string in attrArray:
if(attrArray.index(string) == (len(attrArray) - 1)):
return getattr(baseObj,string)
else:
baseObj = getattr(baseObj,string)
return "failed"
sample is the object and attributeString is, for example object.subObject.attributeYouWant

Use function parameter to construct name of object or dataframe

I would like to use a function's parameter to create dynamic names of dataframes and/or objects in Python. I have about 40 different names so it would be really elegant to do this in a function. Is there a way to do this or do I need to do this via 'dict'? I read that 'exec' is dangerous (not that I could get this to work). SAS has this feature for their macros which is where I am coming from. Here is an example of what I am trying to do (using '#' for illustrative purposes):
def TrainModels (mtype):
model_#mtype = ExtraTreesClassifier()
model_#mtype.fit(X_#mtype, Y_#mtype)
TrainModels ('FirstModel')
TrainModels ('SecondModel')
You could use a dictionary for this:
models = {}
def TrainModels (mtype):
models[mtype] = ExtraTreesClassifier()
models[mtype].fit()
First of all, any name you define within your TrainModels function will be local to that function, so won't be accessible in the rest of your program. So you have to define a global name.
Everything in Python is a dictionary, including the global namespace. You can define a new global name dynamically as follows:
my_name = 'foo'
globals()[my_name] = 'bar'
This is terrible and you should never do it. It adds too much indirection to your code. When someone else (or yourself in 3 months when the code is no longer fresh in your mind) reads the code and see 'foo' used elsewhere, they'll have a hard time figuring out where it came from. Code analysis tools will not be able to help you.
I would use a dict as Milkboat suggested.

How do I use text from a file as a variable name?

How do I use text from a file as a variable name?
I am pulling values out of an excel file.
I am using xlrd and xlutils with python 3.
class employee(object):
def __init__(self, name):
self.name = name
emp_list.append(name)
def bulk_hours(self,sunday=0,monday=0,tuesday=0,wednesday=0,thursday=0,friday=0,saturday=0):
self.sunday = sunday
self.monday = monday
self.tuesday = tuesday
self.wednesday = wednesday
self.thursday = thursday
self.friday = friday
self.saturday = saturday
I'm pulling employees out of a spreadsheet.
I'm trying to use their actual names.
I would love to know any working solution.
Thanks!
Edit: Pardon my ignorance regarding programming and my horrible post.
I'm trying to make a simple program that allows me to load an employees name and work schedule from Excel.
I will also make sure any edits are saved back into the spreadsheet.
The employees are labeled by their names. I'm trying to load their name as a variable so I can do:
John = employee('John')
John.bulk_hours(0,8,8,8,8,8,0)
Stacy = employee('Stacy')
print(John.monday)
I'm aiming to use their name as the variable I can use dot notation on.
Is this feasible? Is their some other way I should approach this?
def load(row):
employee2 = employee(s.cell(row, 0).value)
employee2.bulk_hours(s.cell(row, 1).value, s.cell(row, 2).value, s.cell(row, 3).value, s.cell(row, 4).value,
s.cell(row, 5).value, s.cell(row, 6).value, s.cell(row, 7).value)
print(employee2.saturday)
I'm trying to use a function like this to load multiple employees and their hours.
Could I use a list like this somehow?
worker = ['Joe']
worker[0] = employee('Joe')
worker[0].bulk_hours(0,8,8,8,8,8,0)
print(worker[0].monday)
Thank you for your valuable time.
Override __getattr__ to transparently access an internal dictionary.
class employee(object):
def __init__(self, ...):
self._internal_d = extract_data() # replace extract_data with however you extract CSV values to a dictionary
... # perform other initialization
def __getattr__(self, name):
try:
return self._internal_d[name]
except KeyError:
raise AttributeError()
Optionally, you can implement __setattr__ to allow writing properties.
def __setattr__(self, name, value):
return self._internal_d[name] = value
Explanation: when python does variable assignment and can't find a variable name "normally", it checks if an object has __getattr__. If it does, it calls __getattr__ to get the value with the specified name. Thus, you can have dynamic variable names. Likewise for __setattr__.
You don't want to use variable names comming from the spreadsheet.
or one: variable names are internal to the running program, and are not meant to be exported again to an output file.
It is meaningless that the variable is bamed John to represent John's data when the program is running. For example, let's suppose it would be possible to create a special markup to use teh variable name - say a ? prefix to fetch the name from another variable. Your example would be something like this:
def row_data(emp_name, *args):
?emp_name = employee(emp_name)
?emp_name.bulk_hours(*args)
print(?emp_name.monday)
So, even if at runtime ?emp_name would be exchanged by the contents of the variable name, yur program would still look the same to someone reading the code. So, it makes more sense to simply let the variable be named person or employee or anything, since it can represent any employee (and in fact will, as you loop through the spreadsheet contents, usually the variable will carry the data about one person a time).
That said, there are times when we do want to have data in the program which do have programmatic labeling. but still on those cases - that is what dictionaries are for - create an employees dict, and fill it - and then you can have the names as the keys:
employees = dict()
def row_data(emp_name, name):
person = employee(emp_name)
person.bulk_hours(*args)
employes[emp_name] = person
def print_employeers():
for person_name, person_data in employees.items():
print(person_name, person_data)
As you can see, it is possible to print all employees data without having to type in their names. Also, if it is an interactive program, it is possible to find the data related to a name that is input by the user, using it as the dictionary key.
However if you intend to have a program to generate other Python source files themselves, and end-up with a .py file for each employee. In that case just make use of a templating engine, like Jinja2, or simply use str's format method. (It still hard to imagine why you would need such a thing).
And, just to have a complete answer, it is possible to create dynamic variable names in Python. However, you will be in the exact same situation I described in the first example here.
Global variables for any running code are kept in a regular Python dictionary, which is returned by a call to the globals() function. And similarly, values for local variables are kept in a dictionary that returned by a call to locals() - although these do not behave nicely for variables known at compile time (in that case, the local variables are cached in the frame object, and the locals dict is only synchornized with them when it is read, but they can't be written via locals)
So:
def row_data(emp_name, *args):
globals()[emp_name] = employee(emp_name)
globals()[emp_name].bulk_hours(*args)
print(globals()[emp_name].monday)
will work just as you asked - but it is easy to see it is useless.

Python: Dynamic attribute name generation without exec() or eval()

I'm trying to dynamically create buttons at runtime with PyQT4.7
However, this being my first python program I'm not sure how to get the functionality I want.
I would like to be able to substitute a text string for an attribute name:
i.e.
for each in xrange(4):
myname = "tab1_button%s" % each #tab1_button0, tab1_button1, tab1_button2
#self.ui.tab1_button0 = QtGui.QPushButton(self.ui.tab) <--normal code to create a named button
setattr(self.ui,myname,QtGui.QPushButton(self.ui.tab)) #rewrite of line above to dynamicly generate a button
#here's where I get stuck. this code isn't valid, but it shows what i want to do
self.ui.gridLayout.addWidget(self.ui.%s) % myname
#I need to have %s be tab1_button1, tab1_button2, etc. I know the % is for string substituion but how can I substitute the dynamically generated attribute name into that statement?
I assume there's a basica language construct I'm missing that allows this. Since it's my first program, please take it easy on me ;)
If I interpreted this correctly, I think what you want is this:
self.ui.gridLayout.addWidget(getattr(self.ui,myname))
Give that a go. In Python the following two statements are functionally equivalent (from the link below):
value = obj.attribute
value = getattr(obj, "attribute-name")
For extra context:
http://effbot.org/zone/python-getattr.htm
Just assign the button to a variable so you can both set the attribute and add the widget.
for i in range(4):
name = 'button%d' % i
button = QtGui.QPushButton(...)
setattr(self, name, button)
self.ui.gridLayout.addWidget(button)
Personally I would add the buttons to a list instead of giving them different names.
I think you might benefit from knowledge of lists (commonly called arrays in other languages)
self.buttons = [None, None, None, None]
for each in xrange(4):
self.buttons[each] = QtGui.QPushButton(self.ui.tab)
self.ui.gridLayout.addWidget(self.buttons[each])
For a tutorial on Python lists:
http://effbot.org/zone/python-list.htm

Categories

Resources