3

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

  • 1
    You really don't want to do this. What do you think you would get out of it? Your source code isn't going to magically change when you rename the global variable, so all that code that was expecting to do something with `Nametemp` would get an error unless you quit the program and rewrote it all to use `engine1`. And then, the first time the user types a different string in the entry box, you have to quit and rewrite your program again. – abarnert Apr 24 '18 at 07:49
  • Also, the name of the variable doesn't appear anywhere _but_ in your source code. If you print it out, or attach it to a Label, etc., it's the _value_, not the _variable name_, that the user will see. So that value is all that matters. For more info, see [Ned Batchelder's blog post on this](http://nedbatchelder.com/blog/201112/keep_data_out_of_your_variable_names.html), and, if it's still not clear, [mine](http://stupidpythonideas.blogspot.com/2013/05/why-you-dont-want-to-dynamically-create.html). – abarnert Apr 24 '18 at 07:50
  • I've added a paragraph to the post, I think it explains what I need a bit further – JuanFiscina Apr 24 '18 at 14:44
  • 1
    Your explanation doesn’t explain why you think changing the variable name will be useful. I think all you want to do here is add an instance attribute to the class called `name`, which you can then access like any other attribute to display the name of an engine to the user or use it as a label in a graph or store it persistently in a file or whatever. – abarnert Apr 24 '18 at 15:19
  • Without an [mcve] showing how you intend to access these objects and use them for graphing, etc., it’s hard to suggest how you want to store them, but mostly likely it’s either some collection you can iterate (like a list, or maybe a dict keyed by the name attributes), or two variables named `firstengine` and `secondengine`, or something like that. – abarnert Apr 24 '18 at 15:21
  • I like the idea of the 'name' attribute, makes sense :) I will be giving it a go today, plus the other options suggested :) – JuanFiscina Apr 25 '18 at 06:35

1 Answers1

-2

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.

Programmer S
  • 429
  • 7
  • 21
  • 2
    Do not use `exec()`. There rarely is any need to; here you could just use `globals()[Name] = vtdiagram()`, for example. – Martijn Pieters Apr 24 '18 at 09:04
  • @MartijnPieters Thank you for your feedback. I have made it clear to avoid "exec". – Programmer S Apr 24 '18 at 09:20
  • Hello, Thanks for the reply: I will be looking into it. 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- and this is the vtdiagram class: The reason for using a class is that I have the characteristics of 'engine1' 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 that 'engine1' but only when called. Please keep in mind I am quite new to python. – JuanFiscina Apr 24 '18 at 14:26
  • @SuperS Martijn’s point is that even in the rare cases where you do want to dynamically create variables, you still almost never want to use `exec`. Your code would be much better written as `globals[Name] = vtdiagram()` even if it is needed, so an example showing how to write it with `exec` is counterproductive even with the warning. – abarnert Apr 24 '18 at 15:23
  • 1
    @JuanFiscina: none of that requires that you put those names as global variables. Put them in a dictionary instead, like `parameters = {}`, then `parameters['engine1'] = vtdiagram()`. All interactions then use that dictionary to reach the specific user-defined names. That way, the user could use the name `tkinter` and **not break your program**. – Martijn Pieters Apr 24 '18 at 17:43
  • @MartijnPieters: I like the idea of the dictionary, will give it a go :) I've actually been thinking that I can skip the class altogether and go for a dictionaries instead, but that defeats the purpose of me practicing class usage :D – JuanFiscina Apr 25 '18 at 06:37
  • @abarnert How can an example be "counterproductive even with the warning"? I understand that it is a bad idea to use `exec`, but I wanted to make sure **I answered the question**, as well as explained the best approach. _(I'd probably give priority to answering the question.)_ The question asked if there was "a command that can do such a thing", which there was. I also knew a better way to "achieve the same effect", which I gave. I don't yet know much about dictionaries _(I didn't know anything about them when I first posted the answer)_, but I don't see why my answer was down-voted. – Programmer S Apr 26 '18 at 08:28
  • 1
    @SuperS An example can be counterproductive even with the warning if the warning is insufficient. Your answer suggests that in the rare cases were you want to do something like this, you should use `exec`. But that's wrong. In the rare cases when you want to do something like this, you should use `globals`. The fact that you didn't know your answer is wrong doesn't mean your answer isn't wrong, it means you shouldn't have been trying to answer a question that you don't know the answer to. – abarnert Apr 26 '18 at 15:48
  • @abarnert I thought I did know the answer. I did not initially know about dictionaries. I have researched dictionaries and edited my post. Is it worth an up-vote now, or does it have any more issues? – Programmer S Apr 27 '18 at 08:48