This question already has answers here:
How to dynamically load a Python class
(13 answers)
Closed 11 months ago.
I'm new in python, and need some help here:
The user enters an identifer (project name, client name) through the GUI or controle screen.
This text entered by the user should be the identifier that automatically instantiates a Class:
identifier = Class()
Question: how can I do that in python? (so far as I have seen in videos, every instantiation was done directly from a variable typed inside the code...)
You can use a bunch of if statements to decide which class to instantiate
user_input = input()
if user_input == 'myClass':
identifier = myClass()
elif user_input == 'anotherClass':
identifier = anotherClass()
...
You don't want to just blindly accept any input from the user because of security issues. There is eval and exec, but you would be basically giving the user free reign to also run whatever code they want by using those.
If you have more than a few classes, then instead of if statements you can use a dictionary like so.
inputs_to_classes = { 'myClass': myClass, 'anotherClass': anotherClass }
user_input = input('Enter a class to instantiate: ').lower()
identifier = inputs_to_classes[user_input]()
Or in Python 3.10+ you could use a match expression, but I don't currently have access to Python 3.10.
the correct answer in this case is: don't do this. (and yes, it's possible to do this, but again, don't do this)
Essentially, your user should not even need to be made aware of what variables are, and should absolutely not be controlling what variable names are created in your code dynamically for your objects of these classes.
So, one actual solution that bypasses this issue is to just use a dictionary. take a string from user, that's a key in a dictionary. initialize your class against the key in the dictionary, and just use the key to access the class.
Another, perhaps better solution, is to just have your class take an extra name attribute. when you need to display the name the user entered for the class, you access it on the attribute. Then you do not even need to use a dictionary, you just create the class with your own variable name internally, but always display the user entered name from it's attribute. This separates concerns between what the user knows and what the programmer should deal with.
# Recommended solution
class Project:
def __init__(self, name, other_params):
self.name = name
self.other_params = other_params
def some_method(self, extra_args):
pass
class Client:
def __init__(self, name, other_params):
self.name = name
self.other_params = other_params
def some_method(self, extra_args):
pass
# these can come from your gui instead of input, doesn't matter
project_user_input = input("enter project name: ")
# these can come from your gui instead of input, doesn't matter
client_user_input = input("enter client name: ")
# make the actual objects, your variable names are internal to you,
# and the names themselves should not be tied to business logic.
# use the .name method to access the user-facing names.
project_obj = Project(name=project_user_input, other_params=42)
client_obj = Client(name=client_user_input, other_params=100)
# you can always access user facing names as necessary.
print(f"the project has the name: {project_obj.name}")
print(f"the client has the name: {client_obj.name}")
Decide on gui framework
Learn gui framework
Come back with questions regarding that framework
Related
Firstly, I do apologise as I'm not quite sure how to word this query within the Python syntax. I've just started learning it today having come from a predominantly PowerShell-based background.
I'm presently trying to obtain a list of projects within our organisation within Google Cloud. I want to display this information in two columns: project name and project number - essentially an object. I then want to be able to query the object to say: where project name is "X", give me the project number.
However, I'm rather having difficulty in creating said object. My code is as follows:
import os
from pprint import pprint
from googleapiclient import discovery
from oauth2client.client import GoogleCredentials
credentials = GoogleCredentials.get_application_default()
service = discovery.build('cloudresourcemanager', 'v1', credentials=credentials)
request = service.projects().list()
response = request.execute()
projects = response.get('projects')
The 'projects' variable then seems to be a list, rather than an object I can explore and run queries against. I've tried running things like:
pprint(projects.name)
projects.get('name')
Both of which return the error:
"AttributeError: 'list' object has no attribute 'name'"
I looked into creating a Class within a For loop as well, which nearly gave me what I wanted, but only displayed one project name and project number at a time, rather than the entire collection I can query against:
projects=[]
for project in response.get('projects', []):
class ProjectClass:
name = project['name']
projectNumber = project['projectNumber']
projects.append(ProjectClass.name)
projects.append(ProjectClass.projectNumber)
I thought if I stored each class in a list it might work, but alas, no such joy! Perhaps I need to have the For loop within the class variables?
Any help with this would be greatly appreciated!
As #Code-Apprentice mentioned in a comment, I think you are missing a critical understanding of object-oriented programming, namely the difference between a class and an object. Think of a class as a "blueprint" for creating objects. I.E. your class ProjectClass tells python that objects of type ProjectClass will have two fields, name and projectNumber. However, ProjectClass itself is just the blueprint, not an object. You then need to create an instance of ProjectClass, which you would do like so:
project_class_1 = ProjectClass()
Great, now you have an object of type ProjectClass, and it will have fields name and projectNumber, which you can reference like so:
project_class_1.name
project_class_1.projectNumber
However, you will notice that all instances of the class that you create will have the same value for name and projectNumber, this just won't do! We need to be able to specify values when we create each instance. Enter init(), a special python method colloquially referred to as the constructor. This function is called by python automatically when we create a new instance of our class as above, and is responsible for setting up all the fields of that class. Another powerful feature of classes and objects is that you can define a collection of different functions that can be called at will.
class ProjectClass:
def __init__(self, name, projectNumber):
self.name = name
self.projectNumber = projectNumber
Much better. But wait, what's that self variable? Well, just as before we were able reference the fields of our instance via the "project_class_1" variable name, we need a way to access the fields of our instance when we're running functions that are a part of that instance, right? Enter self. Self is another python builtin parameter that contains a reference to the current instance of the ProjectClass that is being accessed. That way, we can set fields on the instance of the class that will persist, but not be shared or overwritten by other instances of the ProjectClass. It's important to remember that the first argument passed to any function defined on a class will always be self (except for some edge-cases you don't need to worry about now).
So restructuring your code, you would have something like this:
class ProjectClass:
def __init__(self, name, projectNumber):
self.name = name
self.projectNumber = projectNumber
projects = []
for project in response.get('projects', []):
projects.append(ProjectClass(project["name"], project["projectNumber"])
Hopefully I've explained this well and given you a complete answer on how all these pieces fit together. The hope is for you to be able to write that code on your own and not just give you the answer!
I have the following code;
class weather(object):
temperature = 0
humidity = 0
precis = "There is no weather when there plainly should be, please contact an administrator."
cloud = "none"
windDirection = "nnw"
windSpeed = 0
def getWeather(self):
weatherJSON = requests.get('http://www.bom.gov.au/fwo/IDT60901/IDT60901.94970.json')
weatherDoc = json.loads(weatherJSON.text)
temperature = weatherDoc["observations"]["data"][1]["apparent_t"]
humidity = weatherDoc["observations"]["data"][1]["rel_hum"]
windDirection = weatherDoc["observations"]["data"][1]["wind_dir"]
windSpeed = weatherDoc["observations"]["data"][1]["wind_spd_kmh"]
cloud = weatherDoc["observations"]["data"][1]["cloud_type"]
This is a class that has weather features, and contains a function to update them. If I then instantiate the class with
this = weather()
this.getWeather()
The variables in this don't get updated with real world weather. Two questions, why not, and I guess as more of a sub-question, am I doing this right? Should my approach be to use the methods in a class to manipulate an instance of the class?
You've got two fundamental problems here.
First, you're mixing up class attributes—that is, variables that are shared by all instances of the same class—and normal instance attributes—that is, variables that are a part of each instance.
You very rarely want class attributes for anything. You want instance attributes, however, all the time. So, to start with:
class weather(object):
def __init__(self):
self.temperature = 0
self.humidity = 0
self.precis = "There is no weather when there plainly should be, please contact an administrator."
self.cloud = "none"
self.windDirection = "nnw"
self.windSpeed = 0
Notice the self.temperature, not just temperature. That's how you create an instance attribute. It's also how you access or update one.
Second, you're also mixing up local variables—that is, variables that exist while a function is running and then disappear—with instance attributes. But you already know hot to update instance attributes from above. So:
def getWeather(self):
weatherJSON = requests.get('http://www.bom.gov.au/fwo/IDT60901/IDT60901.94970.json')
weatherDoc = json.loads(weatherJSON.text)
self.temperature = weatherDoc["observations"]["data"][1]["apparent_t"]
self.humidity = weatherDoc["observations"]["data"][1]["rel_hum"]
self.windDirection = weatherDoc["observations"]["data"][1]["wind_dir"]
self.windSpeed = weatherDoc["observations"]["data"][1]["wind_spd_kmh"]
self.cloud = weatherDoc["observations"]["data"][1]["cloud_type"]
self.precis = '???'
(I'm not sure what you want to put in precis, but clearly you don't want to leave it as "There is no weather…".)
If you make the second fix without the first, everything will appear to work, but only by coincidence. When you ask for this.temperature, if this has no instance attribute named temperature, Python will automatically look for the class attribute type(this).temperature. If you then add an instance attribute named temperature, it "shadows" the class attribute, so next time you do this.temperature, you get the instance's value.
So, you can use class attributes as a sort of "default value" for instance attributes. But you should only do this if you know what you're doing. (It can get really confusing if, say, you start using mutable values like lists and mutating them in your methods…)
Two questions, why not, and I guess as more of a sub-question, am I doing this right?
You're updating a local variable in getWeather().
You should update the instance variable instead, which can be done by prefixing the variable name with self, in other words, replace:
def getWeather(self):
...
cloud = ...
with
def getWeather(self):
...
self.cloud = ...
Is the indentation in your example broken? Are those variables supposed to be class members of the weather class?
Eitherway, the function is shadowing them, creating new ones locally and therefore not changing the others.
I'm teaching myself how to write a basic game in python (text based - not using pygame). (Note: I haven't actually gotten to the "game" part per-se, because I wanted to make sure I have the basic core structure figured out first.)
I'm at the point where I'm trying to figure out how I might implement a save/load scenario so a game session could persist beyond a signle running of the program. I did a bit of searching and everything seems to point to pickling or shelving as the best solutions.
My test scenario is for saving and loading a single instance of a class. Specifically, I have a class called Characters(), and (for testing's sake) a sigle instance of that class assigned to a variable called pc. Instances of the Character class have an attribute called name which is originally set to "DEFAULT", but will be updated based on user input at the initial setup of a new game. For ex:
class Characters(object):
def __init__(self):
self.name = "DEFAULT"
pc = Characters()
pc.name = "Bob"
I also have (or will have) a large number of functions that refer to various instances using the variables they are asigned to. For example, a made up one as a simplified example might be:
def print_name(character):
print character.name
def run():
print_name(pc)
run()
I plan to have a save function that will pack up the pc instance (among other info) with their current info (ex: with the updated name). I also will have a load function that would allow a user to play a saved game instead of starting a new one. From what I read, the load could work something like this:
*assuming info was saved to a file called "save1"
*assuming the pc instance was shelved with "pc" as the key
import shelve
mysave = shelve.open("save1")
pc = mysave["pc"]
My question is, is there a way for the shelve load to "remember" the variable name assotiated with the instance, and automatically do that << pc = mysave["pc"] >> step? Or a way for me to store that variable name as a string (ex as the key) and somehow use that string to create the variable with the correct name (pc)?
I will need to "save" a LOT of instances, and can automate that process with a loop, but I don't know how to automate the unloading to specific variable names. Do I really have to re-asign each one individually and explicitly? I need to asign the instances back to the apropriate variable names bc I have a bunch of core functions that refer to specific instances using variable names (like the example I gave above).
Ideas? Is this possible, or is there an entirely different solution that I'm not seeing?
Thanks!
~ribs
Sure, it's possible to do something like that. Since a shelf itself is like a dictionary, just save all the character instances in a real dictionary instance inside it using their variable's name as the key. For example:
class Character(object):
def __init__(self, name="DEFAULT"):
self.name = name
pc = Character("Bob")
def print_name(character):
print character.name
def run():
print_name(pc)
run()
import shelve
mysave = shelve.open("save1")
# save all Character instances without the default name
mysave["all characters"] = {varname:value for varname,value in
globals().iteritems() if
isinstance(value, Character) and
value.name != "DEFAULT"}
mysave.close()
del pc
mysave = shelve.open("save1")
globals().update(mysave["all characters"])
mysave.close()
run()
This question already has answers here:
How can you dynamically create variables? [duplicate]
(8 answers)
Closed 8 years ago.
I'm just learning to program and am learning Python as my first language. As an exercise I'm trying to write an address book program. New contact are created by the user using the command prompt. New contacts are object instances of the Contacts class.
I know how to instantiate a class object from within the code, but how do I create one with a variable name based on user input? Say I prompt the user for a name -- how do I take that info and use it for the variable name of my new object?
Thanks!!
From the comments, it turns out you are asking about something that gets asked more than once on here. "How can I create dynamically named variables".
Answer: Don't do this. Chances are there are better ways to solve the problem.
Explanation:
If you were to create dynamically named variables, you don't quite have a good handle to them once they are created. Sure there are ways to check the globals and local scopes to see what is there. But the fact is that you should have definitive control over what is being created.
What you should do is put them into a dictionary:
people = {}
name = raw_input("What name? ") # "person"
people[name] = User(name)
print people
# {'person': <User: "person">}
print people.keys()
# ['person']
This way you are not creating arbitrary variables in your namespace. You now have a dictionary of keys and objects as values. It is also a can of worms to allow a user-supplied input to drive the naming of a variable.
For more info, just search on here for the same topic and see numerous examples of why you should not do this. No matter what examples you see showing you how to use globals(), etc, please take my advise and don't go that route. Love and enjoy..and maybe hug and kiss, your dictionary.
References:
How can you dynamically create variables via a while loop?
Is it possible to "dynamically" create local variables in Python? (DONT DO THIS)
You do not make clear why you would want to instantiate objects of which the name is determined as runtime as you wish. It is important to realize that that is not common practice.
It is possible, though, using the setattr builtin:
setattr(someobject, name, user)
Somewhat more normal usage would be to use a dictionary. So if you have more than one user instance and want to store them, you could store them in a dictionary like below. This would allow you to reference the object by name.
class User(object):
def __init__(self, name):
self.name = name
users = {}
name = raw_input("What name?")
users[name] = User(name)
print users
print users['Andre'].name
Sample output:
What name?Andre
{'Andre': <__main__.User object at 0x7f6b418a8710>}
Andre
I have a problem grasping the OOP concept when it comes to creating objects during runtime. All the educational code that I have looked into yet defines specific variables e.g. 'Bob' and assigns them to a new object instance. Bob = Person()
What I have trouble understanding now is how I would design a model that creates a new object during runtime? I'm aware that my phrasing is probably faulty since all objects are generated during runtime but what I mean is that if I were to start my application in a terminal or UI how would I create new objects and manage them. I can't really define new variable names on the fly right?
An example application where I run into this design issue would be a database storing people. The user gets a terminal menu which allows him to create a new user and assign a name, salary, position. How would you instantiate that object and later call it if you want to manage it, call functions, etc.? What's the design pattern here?
Please excuse my poor understanding of the OPP model. I'm currently reading up on classes and OOP but I feel like I need to understand what my error is here before moving on. Please let me know if there is anything I should clarify.
Things like lists or dictionaries are great for storing dynamically generated sets of values/objects:
class Person(object):
def __init__(self, name):
self.name = name
def __repr__(self):
print "A person named %s" % self.name
people = {}
while True:
print "Enter a name:",
a_name = raw_input()
if a_name == 'done':
break
people[a_name] = Person(a_name)
print "I made a new Person object. The person's name is %s." % a_name
print repr(people)
You don't store each object with a variable name. Variable names are for the convenience of a programmer.
If you want a collection of objects, you use just that - a collection. Use either a list or a dictionary containing object instances, referenced by index or key respectively.
So for example, if each employee has an employee number, you might keep them in a dictionary with the employee number as a key.
For your example, you want to use a model abstraction.
If Person is a model class, you could simply do:
person = new Person()
person.name = "Bob"
person.email = "bob#aol.com"
person.save() # this line will write to the persistent datastore (database, flat files, etc)
and then in another session, you could:
person = Person.get_by_email("bob#aol.com") # assuming you had a classmethod called 'get_by_email'
I'll try to answer as best I can here:
What you're asking about is variable variable names - this isn't in Python. (I think it's in VB.Net but don't hold me to that)
The user gets a terminal menu which allows him to create a new user and assign a name, salary, position. How would you instantiate that object and later call it if you want to manage it, call functions, etc.? What's the design pattern here?
This is how I'd add a new person (Mickey-mouse example):
# Looping until we get a "fin" message
while True:
print "Enter name, or "fin" to finish:"
new_name = raw_input()
if new_name == "fin":
break
print "Enter salary:"
new_salary = raw_input()
print "Enter position:"
new_pos = raw_input()
# Dummy database - the insert method would post this customer to the database
cnn = db.connect()
insert(cnn, new_name, new_salary, new_pos)
cnn.commit()
cnn.close()
Ok, so you want to now get a person from the database.
while True:
print "Enter name of employee, or "fin" to finish:"
emp_name = raw_input()
if emp_name == "fin":
break
# Like above, the "select_employee" would retreive someone from a database
cnn = db.connect()
person = select_employee(cnn, emp_name)
cnn.close()
# Person is now a variable, holding the person you specified:
print(person.name)
print(person.salary)
print(person.position)
# It's up to you from here what you want to do
This is just a basic, rough example, but I think you get what I mean.
Also, as you can see, I didn't use a class here. A class for something like this would pretty much always be a better idea, but this was just to demonstrate how you'd change and use a variable during runtime.
You would never do Bob = Person() in a real program. Any example that shows that is arguably a bad example; it is essentially hard-coding. You will more often (in real code) do person = Person(id, name) or something like that, to construct the object using data you obtained elsewhere (read from a file, received interactively from a user, etc.). Even better would be something like employee = Person(id, name).