Need Clarification on Using OOP and DRY Method in Python
Question: I'm trying to keep my code clean by applying OOP and DRY method; however, I
found myself stuck with the following questions.

1) Since **checkremote** and **backup** method are dependent on the
**sshlogin** method, is there another way to write it so that my object is
fully initialized?

2) If there isn't a better way, where do I write the procedure for the
PhoneBook Class to execute in the following manner (1 - checklocal, 2 -
sshlogin, 3 - checkremote, 4 - backup)? In main?

    
    
    class PhoneBook:
    
        def __init__ (self, name, phone_number, birthdate, location):
            self.name = name
            self.phone_number = phone_number
            self.birthdate = birthdate
            self.location = location
            self.ssh = None
    
        def checklocal (self):
            # Check local phonebook for existing names
            pass
    
    
        def sshlogin (self):
            # SSH into remote server
    
    
        def checkremote (self):
            # Check remote phonebook for existing names
            pass
    
    
        def backup (self):
            # Backup remote phonebook
    


Answer: In this case, you probably want to use a context manager and the `with`
keyword.

Since using a `Phonebook` object requires a "set up" phase beforehand, you
want to make sure that is handled correctly every time you use it. So you
would able to write code like this:

    
    
    with Phonebook(name, phone_number, birthdate, location) as phbk: 
        #do stuff with the phonebook
        phbk.add(name, phone_number, birthdate, location)
    

All your setup phase steps - checking the local copy, connecting (and
disconnecting) the ssh session, checking the remote copy, backup, etc. - would
happen "behind the scenes" as part of the setup/tear down of the context
manager (in other words, the `with` statement takes care of all that). This is
similar to how you are supposed to use `open()`:

    
    
    with open('myfile') as file:
        lines = file.readlines()
    

Cleanly opening and closing the file happens "behind the scenes",
automagically. This is likely what you want to happen with your phonebook.

To get the context manager working, you use the python `__enter__` and
`__exit__` magic methods. Might look something like this:

    
    
    class PhoneBook:
    
        def __init__ (self, name, phone_number, birthdate, location):
            self.name = name
            self.phone_number = phone_number
            self.birthdate = birthdate
            self.location = location
            self.ssh = None
    
        def sshlogin(self):
            # SSH into remote server
    
        def sshlogout(self):
            # SSH out of remote server
    
        def checklocal(self):
            # Check local phonebook for existing names
            pass        
    
        def checkremote (self):
            # Check remote phonebook for existing names
            pass            
    
        def backup (self):
            # Backup remote phonebook
    
        def __enter__(self):
            self.checklocal()
            self.sshlogin()
            self.checkremote()
            self.backup()
            return self
    
        def __exit__(self, cls, value, traceback):
            self.sshlogout()
            return False
    

As for your `Add` (and `Delete`) class, it really shouldn't be a class. It
should probably either be:

  * a method of the `Phonebook` class, like so:
    
        class Phonebook:
        def __init__(self):
            self.ssh = None
        def add(self, name, phone_number, birthdate, location):
            self.name = name
            self.phone_number = phone_number
            self.birthdate = birthdate
            self.location = location
    

or

  * a generic function, like so: 
    
        def Add(phbk, name, phone_number, birthdate, location):
        # add to remote phonebook
    

A further point: your `Phonebook` class isn't named very well. Based on the
`__init__` method, it's more of a phonebook entry. You may want to consider
implementing your phonebook (as a context manager, as explained above)
separately as a container of some kind to hold your entries (including the
`add` and `delete` methods), and focus the `Phonebook` class you have (renamed
as `Entry` or something) on being more of a phonebook `Entry` object that can
be added to your new `Phonebook` object, in which case the add method might be
more like this:

    
    
        def add(self, entry):
            try:
                self.entries.append(entry)
            except AttributeError:
                self.entries = [entry] 
    

One other thing: I noticed you're using tab characters to define your classes,
functions, and what not. This is [against the recommended
practice](http://stackoverflow.com/questions/120926/why-does-python-
pep-8-strongly-recommend-spaces-over-tabs-for-indentation) \- use 4 spaces
instead.

A further suggestion: for something so simple as my suggested `Entry` class,
you might just want to use a `collections.namedtuple`, like so:

    
    
    from collections import namedtuple as nt
    Entry = nt('Entry', 'name phone_number birthdate location')
    

Now you can do stuff like this:

    
    
    e = Entry('rick', '8675309', '1/1/2001', '125 Sesame St`)
    print(e.name)
    

