FlintSable
7/3/2019 - 9:50 PM

Class In Class

class Contact:
    """ example that uses an inner (nested) class """

    # class ("static") members and intended constants
    DEFAULT_NAME = "(no name assigned)"
    MIN_NAME_LEN = 2
    MAX_NAME_LEN = 30
    DEFAULT_PH_NUM = "0001112222"

    # initializer ("constructor") method -------------------------------
    def __init__(self,
                 the_name=DEFAULT_NAME, the_ph_num=DEFAULT_PH_NUM):
        # instance (attributes
        if not self.set_name(the_name):
            self.name = Contact.DEFAULT_NAME

        # since this calls Phone constructor, we need not test return val
        self.phone = self.Phone(the_ph_num)

    # mutator ("set") methods ------------------------------------------
    def set_name(self, the_name):
        if type(the_name) != str \
                or \
                not (Contact.MIN_NAME_LEN <= len(the_name) <= Contact.MAX_NAME_LEN):
            return False
        # else
        self.name = the_name
        return True

    # sets the phone  from client **string**, not from Phone object
    def set_phone(self, the_ph_str):
        if not self.phone.set_phone_num(the_ph_str):
            return False
        # else
        return True  # if good param, mutator call already set

    # accessor ("get") methods ----------------------------------------
    def get_name(self):
        return self.name

    # returns the phone **string**, which most clients would expect
    def get_phone(self):
        return self.phone.to_string()

    # returns the phone **object**, if client wants that level of detail
    def get_phone_object(self):
        return self.phone

    # BEGIN INNER CLASS Phone -------------------------------------------
    class Phone:
        """ Phone is nested in Contact """

        # class ("static") members and intended constants
        DEFAULT_NUM = "7778889999"
        VALID_NUM_LEN = 10

        # initializer ("constructor") method --------------------------
        def __init__(self, the_num=DEFAULT_NUM):

            # instance attributes
            if not self.set_phone_num(the_num):
                self.ph_num = Contact.DEFAULT_PH_NUM

        # mutator method --------------------------
        def set_phone_num(self, the_ph_str):

            # first check that it's a string
            if type(the_ph_str) != str:
                return False
            # throw away non-numerics
            pure_number = self.extract_numeric_digits(the_ph_str)
            # check length
            if len(pure_number) != Contact.Phone.VALID_NUM_LEN:
                return False

            # else (passed all tests)
            self.ph_num = pure_number
            return True

        # accessor methods --------------------------
        def get_phone_num(self):
            return self.ph_num

        def to_string(self):
            """ turn '1234567890' into '(123)456-7890' """

            # build slowly for clarity -- could be done in one long statement
            ret_str = "("
            ret_str += self.ph_num[0:3]
            ret_str += ")"
            ret_str += self.ph_num[3:6]
            ret_str += "-"
            ret_str += self.ph_num[6:10]

            return ret_str

        # helper for vetting Phone numbers  --------------------------
        @staticmethod
        def extract_numeric_digits(the_num):
            """ store only digits '213-555-1212' becomes '2135551212'
                returns None if non-string, else
                the string minus non-numerics """
            if type(the_num) != str:
                return None
            # else
            the_length = len(the_num)
            number = ""
            for k in range(0, the_length):
                next_digit = the_num[k]
                if next_digit.isdigit():
                    number = number + next_digit
            return number

    # END INNER CLASS Phone -------------------------------------------


# client --------------------------------------------

# instantiate some Contacts with illegal and default phone nums #s ...
con_1 = Contact("Harsha", "234")
con_2 = Contact("Alfonso")

print("Nested class objects instantiated directly from main -----:")
bad_phone = Contact.Phone("bad bad number")
default_phone = Contact.Phone()
print(" Bad param gets Contact default: " + bad_phone.get_phone_num()
      + "\n No param gets Phone default: " + default_phone.get_phone_num())

# show the Contacts right after instantiation ---------------------
print("\nBefore mutators -------------------:")
print(("Contact #1:"
       + "\n    name: {}"
       + "\n    phone: {}").
      format(con_1.get_name(),
             con_1.get_phone()))
print(("Contact #2:"
       + "\n    name: {}"
       + "\n    phone: {}").
      format(con_2.get_name(),
             con_2.get_phone()))

# try to mutate some Contact objects, legally and illegally
con_1.set_name("Stephen Hawking")  # should work

# ok to access either the Contact mutator or the Contact.Phone mutator
con_1.set_phone("(605)  555-1212")  # should work
con_1.phone.set_phone_num("123-3333")  # should fail - too short

# contact #2 setters invoked (one good, one bad):
con_2.set_name("waytoolong waytoolong waytoolong")  # should fail
con_2.phone.set_phone_num("123-456-7890")  # should work

# show the Contacts  after mutator calls  ---------------------
print("\nAfter mutators -------------------:")
print(("Contact #1:"
       + "\n    name: {}"
       + "\n    phone: {}").
      format(con_1.get_name(),
             con_1.get_phone()))
print(("Contact #2:"
       + "\n    name: {}"
       + "\n    phone: {}").
      format(con_2.get_name(),
             con_2.get_phone()))

# compare the two get_phone_xxx() accessors  ---------------------
show_me = con_1.get_phone()
print("\nResult of get_phone(): ", show_me)
show_me = con_1.get_phone_object()
print("\nResult of get_phone_object(): ", show_me,
      "\n(that was ugly...)")
print("(Better things to do with the returned object): \n",
      show_me.get_phone_num(),
      "\n      or \n",
      show_me.to_string())