Python classes: how to handle "future" instances - python

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

Related

Why shouldn't one dynamically generate variable names in 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.

Going through list from class in python

For a school project I have to write a python program that records student records and display them.
My school said she wanted a class that represents the school register but I don't know understand how I do that.
I created the following:
class SchoolRegister:
def __init__(self):
ListOfPupils = {}
def REgisterPupil(self, lastname, firstname, schoolID):
ListOfPupils[schoolID] = (lastname, firstname)
sr = SchoolRegister()
sr.REgisterPupil("Thomas", "Lucy", 1)
sr.REgisterPupil("Percival", "Jenny", 2)
I would like to be go through list so I get list out:
1, Thomas, Lucy
2, Percival, Jenny
I searched and found a way of making class act as pupil list thingy, so I can do a for bod in sr but I don't understand it :(
This problem presents a platform upon which we can discuss some interesting Object Oriented programming concepts! We would ideally like to map the real world with code by creating models of the data that represent the real world. This is the inherit value that Object Oriented programming affords us.
Let's use your example.
Our focus is to be able to create records of students that exist (perhaps we should model a student and create parameters which define what a student means to us) and register (we would ideally like to be able to add and remove students from it and be able to retrieve the state of our student body) them in a central place that we can access.
So we can go ahead and implement a simple Student class that represents a student as far as we are concerned (hard to really quantify human beings on a few factors, so let's narrow our focus :D):
class Student:
def __init__(self, id, first_name, last_name):
self.id = id
self.first_name = first_name
self.last_name = last_name
We now have the ability to create objects that represent a student and store that information in a structured way. Now moving on to the the school register, let's think about what sort of data we need. How can we have full awareness of all of the students that exist? A likely data structure we could use could be a list! This grants us the option to (1) store all of the student's that exist (2) Maintain the order in which the student were inserted into the list (or perhaps enrolled at the school).
What if we want to be able to quickly access a Student object though? Perhaps we'd like to know more about a student in the register and we have access to their id. Well in that case we could use a dict which affords us easy student lookups using some sort of unique identifer (i.e. an id).
Let's go ahead and implement a simple example of that:
class SchoolRegister:
def __init__(self):
self.students = {}
def register_student(self, student):
self.students[student.id] = student
def get_student_by_id(self, id):
return self.students[id]
Now we can (1) create a Student, (2) add it to our records, (3) lookup that student information with a key (their id) as shown below:
school_register = SchoolRegister()
john_doe = Student(0, 'John', 'Doe')
school_register.register_student(john_doe)
school_register.get_student_by_id(0) # John Doe!
Suggested Readings:
Classes in Python
What is object oriented programming?

Iterating over "Self" Variable in Class (Attribute Error)

So I'm not sure if the title even makes sense but basically I have a class that keeps track of friends (like an address book), and I'm at the part where I need to create a function that adds friends to a set of existing names. So I've got the beginning of my code as:
class SocialAddressBook:
def __init__(self):
self.book= []
def addName(self, name, address):
self.book.append([name, address, set()])
"""Adds name to address book, with address and no friends"""
which runs fine. and then the part that gives me 'list' object has no attribute 'list' error:
def addFriend(self, name, friend):
for k in range(len(self.book.list[k])):
if self.book[k][0] == name:
my idea was to iterate over a list and add onto the existing list, but syntax is definitely throwing me for a loop (no pun intended) and I'm not sure how I should go about this now.
some test code:
a.addFriend('Fred', 'Barb'); a.addFriend('Fred', 'Sue')
a.addFriend('Barb', 'Jane'); a.addFriend('Jane', 'Emma')
a.addFriend('Jane', 'Mary'); a.addFriend('Emma', 'Lisa')
Thank you!
Your for loop in your addFriend method is incorrect. Specifically, self.book.list[k].
First off, self.book is a list, and lists do not have a list property. Second, you can't use k there as it does not yet exist.
If you want to loop over a list, why not just do that? You don't need to use range().
for book in self.book:
if book[0] == name:
pass
If you can, your solution should be implemented using a dictionary instead of a list to improve performance of your address book. Adding names is trivial, and other than the error checking involved, adding friends is also trivial. For a more complete solution, a remove_name method has been added showing how to correctly remove someone.
import collections
def main():
book = SocialAddressBook()
# Add people to the address book.
book.add_name('Fred', 'fred#gmail.com')
book.add_name('Barb', 'barb#gmail.com')
book.add_name('Jane', 'jane#gmail.com')
book.add_name('Emma', 'emma#gmail.com')
book.add_name('Sue', 'sue#gmail.com')
book.add_name('Mary', 'mary#gmail.com')
book.add_name('Lisa', 'lisa#gmail.com')
# Add friends connections in the address book.
book.add_friend('Fred', 'Barb')
book.add_friend('Fred', 'Sue')
book.add_friend('Barb', 'Jane')
book.add_friend('Jane', 'Emma')
book.add_friend('Jane', 'Mary')
book.add_friend('Emma', 'Lisa')
class SocialAddressBook:
def __init__(self):
self.__book = {}
def add_name(self, name, address):
"""Adds name to address book with address and no friends."""
self.__book[name] = BookEntry(address, set())
def add_friend(self, name, friend):
"""Adds a friend to the person referenced by name."""
if friend not in self.__book:
raise ValueError(f'{friend!r} is not in the address book yet')
self.__book[name].friends.add(friend)
def remove_name(self, name):
"""Completely delete someone from the address book."""
del self.__book[name]
for book_entry in self.__book.values():
book_entry.friends.discard(name)
BookEntry = collections.namedtuple('BookEntry', 'address, friends')
if __name__ == '__main__':
main()
Please read PEP 8 -- Style Guide for Python Code soon if possible. Code written in Python should conform to the style guide to promote maintainability and to encourage others to read your code and answer your questions. In the future, please create a Minimal, Complete, and Verifiable example of your problem before asking your question.

Creating a List in python for Google App Engine

Before i go on, let me say that i have searched the Internet for clarification before i am asking it here.I have a class
class Course(ndb.model):
tutor = ndb.StringProperty(required=True)
...
and a Student class.
I want to include in the Course class a list of Students (represented by their id)registered on the course.From my search, i came across options like StringListProperty() from this website and class ListProperty(item_type, verbose_name=None, default=None, ...) from the Google tutorial on Types and Property Classes.I am still confused as to which is the right way to do this.I need a layman's explanation and possibly a guide to where i can find a tutorial with example.Thanks
You've got a bunch of options, but probably the most straight forward is to use a ndb.KeyProperty with repeated=True. The values will be the key of your particular student. e.g.:
class Student(ndb.Model):
name = ndb.StringProperty()
class Course(ndb.Model):
students = ndb.KeyProperty(repeated=True)
def create_course(students):
"""Create a new course object and return it.
Args:
students: iterable of `Student` model instances.
"""
c = Course()
c.students = [s.key for s in students]
return c

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