3

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.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Rahjael
  • 93
  • 1
  • 1
  • 6
  • On SO, it's best to stick to one question per question. But as for your 2nd question, when you do `Contact('Bob', 'Stevens', '32165497')` like that, an instance gets created, but because you didn't assign it or save it in a list etc it gets thrown away, so you can't access it. – PM 2Ring Apr 29 '18 at 14:55
  • 1
    BTW, you should _seriously_ consider learning Python 3, Python 2 will reach its official End Of Life in 2020. Python 3 has numerous improvements, especially when you're working with classes. – PM 2Ring Apr 29 '18 at 14:59
  • I started with Python 2 only because the first book I tried was an old version of "Learn Python the Hard Way". I abandoned it right after he starts talking about OOP. He assumes too much or I'm not smart enough for that book... either way, now I am sort of going on on my own, and I'm already considering switching to Python 3, the sooner the better... right now I know so little that the only difference seems to be the print() function :D (of course it isn't, but I don't know better at this point) – Rahjael Apr 29 '18 at 16:06
  • FWIW, the SO Python Chat room regulars [do not recommend LPTHW](http://sopython.com/wiki/LPTHW_Complaints); there are several problems with this book. FWIW, you can get Python 3 `print` and division behaviouer in Python 2.6+ by putting `from __future__ import print_function, division` at the top of your script, before any other imports. But it's far better to use the actual Python 3 interpreter. And the sooner you switch over, the better. That way you won't have to unlearn various "unfortunate" features of Python 2 that Python 3 has rectified. – PM 2Ring Apr 29 '18 at 16:21
  • Here's a short list of Python 3 learning resources: https://sopython.com/wiki/What_tutorial_should_I_read%3F – PM 2Ring Apr 29 '18 at 16:23

2 Answers2

2

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

PM 2Ring
  • 54,345
  • 6
  • 82
  • 182
  • Starting to deal with files as a database is exactly why I thought of this exercise, I will get there ;) I have yet to see what that defaultdict(list) is, but the rest seems pretty clear and it helps a lot, thanks! How come "Doe" as a key doesn't get overwritten? Is it because of the defaultdict? (I will check the docs on my own anyway, don't need an extensive reply, don't worry :) ) – Rahjael Apr 29 '18 at 15:47
0

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'))
Jan K
  • 4,040
  • 1
  • 15
  • 16
  • So, in idiomatic language, I end up with "my_database", which is an instance with attribute "inner_list", which is a list in which other instances are stored. Am I right? But none of those instances have a name. I still don't understand how to get rid of "contact1" etc altogether. Isn't there a way to let the user choose the name of the instance which will be created? Also couldn't I do the same that you did by just adding a list to the class Contact and a method to add instances to that list, without adding the class ContactDatabase? – Rahjael Apr 29 '18 at 15:20
  • @Rahjael Seriously, you **don't** want users to start naming the variables in your program, that way lies madness. See https://stackoverflow.com/questions/1373164/how-do-i-create-a-variable-number-of-variables and http://stupidpythonideas.blogspot.com.au/2013/05/why-you-dont-want-to-dynamically-create.html – PM 2Ring Apr 29 '18 at 15:35
  • 1
    @pm2ring Thanks for the references, I will check those. But let me clarify: I didn't mean to really let the final "user" choose variable names... I was just struggling with the concept of the nameless objects list. After your example above I think I'm starting to get the idea. Basically you drop the variable name altogether, and start referring to the nameless objects recalling the dict key or the instance's attributes. Is it right? I think I was missing this conceptual jump in paradigms. – Rahjael Apr 29 '18 at 15:58
  • 1
    Well, the example shows a way how to avoid assigning a class instance to a variable and simply store it in a nice way that might be useful in your application. The variable name itself is irrelevant and arbitrary - the actual names contact1 and contact2 are never stored anywhere. What is stored is the instantied class. – Jan K Apr 29 '18 at 17:30