Why shouldn't one dynamically generate variable names in python? - python

Right now I am learning Python and struggling with a few concepts of OOP, one of that being how difficult it is (to me) to dynamically initialize class instances and assign them to a dynamically generated variable name and why I am reading that I shouldn't do that in the first place.
In most threads with a similar direction, the answer seems to be that it is un-Pythonic to do that.
For example generating variable names on fly in python
Could someone please elaborate?
Take the typical OOP learning case:
LOE = ["graham", "eric", "terry_G", "terry_J", "john", "carol"]
class Employee():
def __init__(self, name, job="comedian"):
self.name = name
self.job = job
Why is it better to do this:
employees = []
for name in LOE:
emp = Employee(name)
employees.append(emp)
and then
for emp in employees:
if emp.name == "eric":
print(emp.job)
instead of this
for name in LOE:
globals()[name] = Employee(name)
and
print(eric.job)
Thanks!

If you dynamically generate variable names, you don't know what names exist, and you can't use them in code.
globals()[some_unknown_name] = Foo()
Well, now what? You can't safely do this:
eric.bar()
Because you don't know whether eric exists. You'll end up having to test for eric's existence using dictionaries/lists anyway:
if 'eric' in globals(): ...
So just store your objects in a dictionary or list to begin with:
people = {}
people['eric'] = Foo()
This way you can also safely iterate one data structure to access all your grouped objects without needing to sort them from other global variables.

globals() gives you a dict which you can put names into. But you can equally make your own dict and put the names there.
So it comes down to the idea of "namespaces," that is the concept of isolating similar things into separate data structures.
You should do this:
employees = {}
employees['alice'] = ...
employees['bob'] = ...
employees['chuck'] = ...
Now if you have another part of your program where you describe parts of a drill, you can do this:
drill['chuck'] = ...
And you won't have a name collision with Chuck the person. If everything were global, you would have a problem. Chuck could even lose his job.

Related

I can't think of a way to aviod dynamic variable in this case

EDIT BELOW
I read a lot of discussion about dynamic variable in python and a lot have shown that they are things that you generally shouldn't do. However I have a case that I really can't think of a way to avoid it (and hence why I am here)
I am currently reading in a text file that stores the information of different members of a store. Each members have their own information: like their phone number, email, points in their accounts, etc. I want to create a class and objects that stores this information. Don't worry this is just a part of an assignment and they are not real people. Here is the sample code:
class Member:
def __init__(self, name, phoneNumber, email, points):
self.name = name
self.phoneNumber = phoneNumber
self.email = email
self.points = points
self.totalPointsSpent = 0
#There are methods below that will return some calculated results, like total points
spent or their ranking. I will not show those here as they are irrelevant
And for each member in the file will be read in and create an object out of it. For example if there are 5 members in that file, five objects will be created, and I want to name them member1, member2, etc. However, this will be confusing when accessing it, as it will be hard to tell them apart, so I add them to a dictionary, with their memberID as the key:
dictonary[memberID] = member1
dictonary[memberID] = member2 #and so on
which would result in a dictionary that look like something like this:
dictionary = {'jk1234':member1,'gh5678':member2,...#etc}
This is the interesting thing about python is the fact that the dictionary value does not need to be a value, it can be an object. This is something new to me, coming from Java.
However, here is the first problem. I do not know how many member are there in a file, or I should say, the number of members in the file varies from file to file. If the number is 5, I need 5 variables; if there are 8, I need 8, and so on. I have thought of using a while loop, and the code will look something like this:
a = len(#the number of members in the file.)
i = 0
while i <= a:
member + i = Member(#pass in the information)
but the '+' operator only works for strings when combining names, not for identifiers. And thus cannot work (I think).
Many solution I read has indicated that I should use a dictionary or a list in such case. However, since my variables are pointing towards a class object, I cannot think of a way to use list/dictionary as the implementation.
My current solution is to use a tuple to store the members information, and do the calculations elsewhere (i.e. I took out the class methods and defined them as functions) so that the dictionary looks like this:
dictionary = {'jk1234': (name, phoneNumber, email, points),'gh5678':(name, phoneNumber, email, points),...#etc}
However given the amount of information I need to pass in it is less than ideal. My current algorithm works, but I want to optimized it and make it more encapsulated.
If you made this far I appreciate it, and would like to know if there is a better algorithm for this problem. Please excuse me for my less than a year of Python experience.
EDIT: Okay I just discovered something really interesting that might seems basic to experienced Python programmers. That is you can pass the values inside the dictionary, instead of naming a variable exclusively. For my problem, that would be:
dictionary = {'jk1234': Member(name, phoneNumber, email, points),'gh5678':Member(name, phoneNumber, email, points),...#etc}
#and they are retrievable:
dictionary['jk1234'].get_score() #get_score is a getter function inside the class
And it would return the proper value. This seems to be a good solution. However, I would still love to hear other ways to think about this problem
It looks like you are on the right track with your update.
It's not clear to me if your MemberID is a value for each member, but if it is, you can use it in a loop creating the objects
d = {}
for member in members_file:
d[MemberID] = Member(name, phoneNumber, email, points)
This, of course, assumes that MemberID is unique, otherwise you will overwrite existing entries.
You could also use a list instead
l = []
for member in members_file:
l.append(Member(name, phoneNumber, email, points))
Further, you could also do the above with list/dict comprehensions which are basically just condensed for-loops.
Dict-comprehension
d = {MemberID: Member(name, phoneNumber, email, points) for member in members_file}
List-comprehension
l = [Member(name, phoneNumber, email, points) for member in members_file]
Whether a list or a dictionary makes more sense, is up to your use-case.
Also note that the above assumes various things, e.g. on what form you get the data from the file, since you did not provide that information.
You can create a dictionary with a for loop:
d = {}
for i in the_members_in_the_file:
d[i] = Member(your, parameters, that\'s, inputted)
You will get your expected dictionary.

Python classes: how to handle "future" instances

I'm teaching myself Python (2.7, no previous coding experience) and I've just started dealing with classes and OOP concepts. As an exercise, I'm trying to code a very simple address book. I think I managed to understand the basics of classes and instances, but what I'm finding hard to grasp is how to further develop the level of abstraction at this point.
Trying to explain better, say I have this, which is often the base example many tutorials use to introduce classes:
class Contact(object):
def __init__(self, name, surname, phone):
self.name = name
self.surname = surname
self.phone = phone
contact1 = Contact('Mark', 'Doe', '123456789')
contact2 = Contact('Sally', 'Preston', '456789123')
So far so good, I can do many other interesting things using contact1.attribute or other methods. No problem here.
What I'm having trouble understanding is the following:
Question 1:
I don't know how many contacts I will have. How do I make a method, say, create_contact(), that makes me create a new contact and store it in a list/dict, if I don't know how many I will have? How do I call it? I can't understand how to make it so that I can create a new instance without hardcoding its name, like "contact1" etc. How do I make the line with "contact1" and "contact2" a dynamic thing?
I tried solving the problem using a list as a class variable. Something like (assuming "contact_list" already exists as a class variable):
Contact.contact_list.append(Contact('Mark', 'Doe','123456789')) # obviously I'd use raw_input instead of 'Mark' etc, but I avoided it here for readability
But I end up with a list of nameless objects, and my brain has a hard time dealing with it. I can access them with list indexes, but I'm not sure I'm on the right track here... any help would be most appreciated.
Question 2: (somewhat related, to better understand the issue)
if in the python CLI I put something like (assuming the previous block defining the class has already been run):
>>> Contact('Bob', 'Stevens', '32165497')
My understanding is that an instance of Contact() does indeed get created, with those attributes... but it has no name. How do I access it? (How do I even know it exists? Is there a way to list all existing instances relative to a certain class?)
I hope I made some sense. Thanks in advance for any kind of help.
There's nothing wrong with having "nameless" instances that get stored in a collection, but I agree that it can be hard to wrap your head around at first. ;) You don't need to know how many contacts you'll be creating, since the Python collection types are all dynamic, so you don't need to specify the size in advance, they'll grow to accomodate the data you feed them.
Here's a demo that uses your Contact class to create a simple phone book in a dictionary of lists. We save each contact both under the first name and the surname, so we can find contacts by either name. The values of the dictionary are lists so we can handle multiple people having the same name.
I added a __repr__ method to Contact to make it easy to display the contents of a Contact instance.
from collections import defaultdict
class Contact(object):
def __init__(self, name, surname, phone):
self.name = name
self.surname = surname
self.phone = phone
def __repr__(self):
return '{name} {surname}: {phone}'.format(**self.__dict__)
phonebook = defaultdict(list)
data = [
('Mark', 'Doe', '123456789'),
('Sally', 'Preston', '456789123'),
('John', 'Doe', '789123456'),
]
for name, surname, phone in data:
contact = Contact(name, surname, phone)
phonebook[name].append(contact)
phonebook[surname].append(contact)
for key, val in phonebook.items():
print(key, val)
output
Mark [Mark Doe: 123456789]
Doe [Mark Doe: 123456789, John Doe: 789123456]
Sally [Sally Preston: 456789123]
Preston [Sally Preston: 456789123]
John [John Doe: 789123456]
Another option is to make phonebook a class attribute of Contact.
Of course, to make this program really useful, we need to be able to save the phonebook to disk, and to be able to load it back in. There are various ways to do that, eg by saving the data to a CSV or JSON file, or to a pickle file. But those are topics for another question. ;)
Q1:
You can create another class that will serve as a database
class Contact(object):
def __init__(self, name, surname, phone):
self.name = name
self.surname = surname
self.phone = phone
class ContactDatabase:
def __init__(self, *args):
self.inner_list = list(args)
def add_contact(self, new_contact):
self.inner_list.append(new_contact)
# Initial contacts
contact1 = Contact('Mark', 'Doe', '123456789')
contact2 = Contact('Sally', 'Preston', '456789123')
# Creating a database
my_database = ContactDatabase(contact1, contact2)
# Adding new contacts later
my_database.add_contact(Contact('Jim', 'Miller', '111223123'))

Names of instances and loading objects from a database

I got for example the following structure of a class.
class Company(object):
Companycount = 0
_registry = {}
def __init__(self, name):
Company.Companycount +=1
self._registry[Company.Companycount] = [self]
self.name = name
k = Company("a firm")
b = Company("another firm")
Whenever I need the objects I can access them by using
Company._registry
which gives out a dictionary of all instances.
Do I need reasonable names for my objects since the name of the company is a class attribute, and I can iterate over Company._registry?
When loading the data from the database does it matter what the name of the instance (here k and b) is? Or can I just use arbitrary strings?
Both your Company._registry and the names k and b are just references to your actual instances. Neither play any role in what you'd store in the database.
Python's object model has all objects living on a big heap, and your code interacts with the objects via such references. You can make as many references as you like, and objects automatically are deleted when there are no references left. See the excellent Facts and myths about Python names and values article by Ned Batchelder.
You need to decide, for yourself, if the Company._registry structure needs to have names or not. Iteration over a list is slow if you already have a name for a company you wanted to access, but a dictionary gives you instant access.
If you are going to use an ORM, then you don't really need that structure anyway. Leave it to the ORM to help you find your objects, or give you a sequence of all objects to iterate over. I recommend using SQLAlchemy for this.
the name doesn't matter but if you are gonna initialize a lot of objects you are still gonna make it reasonable somehow

Creating dynamically named variables from user input [duplicate]

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

Creating objects during runtime in Python

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).

Categories