Error in implementing inheritance


#1

Below is my classes code. I'm doing it as part of an exercise to create a cipher to encode/decode messages. Getting an error that I'm unable to rectify. Please help.

What am I doing here?

There are two classes: Message(parent class) and PlaintextMessage(daughter) class. All I've to do is build a dictionary which stores the value of the strings.ascii_letters value for every value of an integer(input). It shifts the alphabet downstream by that many inputs.

After that when that dictionary is applied to an input string, each character is shifted accordingly and all non ascii_letters are untouched. Below is the code.

import string
class Message(object):
    ### DO NOT MODIFY THIS METHOD ###
    def __init__(self, text):
        '''
        Initializes a Message object
                
        text (string): the message's text

        a Message object has two attributes:
            self.message_text (string, determined by input text)
            self.valid_words (list, determined using helper function load_words
        '''
        self.message_text = text
        #self.valid_words = load_words(WORDLIST_FILENAME)

    ### DO NOT MODIFY THIS METHOD ###
    def get_message_text(self):
        '''
        Used to safely access self.message_text outside of the class
        
        Returns: self.message_text
        '''
        return self.message_text

    
        
    def build_shift_dict(self, shift):
        '''
        Creates a dictionary that can be used to apply a cipher to a letter.
        The dictionary maps every uppercase and lowercase letter to a
        character shifted down the alphabet by the input shift. The dictionary
        should have 52 keys of all the uppercase letters and all the lowercase
        letters only.        
        
        shift (integer): the amount by which to shift every letter of the 
        alphabet. 0 <= shift < 26

        Returns: a dictionary mapping a letter (string) to 
                 another letter (string). 
        '''
        cipher_dict = {}        
        char_list = string.ascii_letters
        small_char_list = string.ascii_lowercase
        big_char_list = string.ascii_uppercase
        for item in char_list:
            if (item in small_char_list) and chr(ord(item) + shift) in small_char_list:
                cipher_dict[item] = chr(ord(item) + shift)
            elif (item in big_char_list) and chr(ord(item) + shift) in big_char_list:
                cipher_dict[item] = chr(ord(item) + shift)
            elif (item in big_char_list) and  (ord(item) + shift) > 90:
                diff = ord(item) + shift - 90
                cipher_dict[item] = chr(ord('A') + diff - 1)
            elif (item in small_char_list) and  (ord(item) + shift) > 122:
                diff = ord(item) + shift - 122
                cipher_dict[item] = chr(ord('a') + diff - 1)
            else:
                continue
                
        
        return cipher_dict

    def apply_shift(self, shift):
        '''
        Applies the Caesar Cipher to self.message_text with the input shift.
        Creates a new string that is self.message_text shifted down the
        alphabet by some number of characters determined by the input shift        
        
        shift (integer): the shift with which to encrypt the message.
        0 <= shift < 26

        Returns: the message text (string) in which every character is shifted
             down the alphabet by the input shift
        '''
        cipher_dict = self.build_shift_dict(shift)
        output_text = ''
        for item in self.message_text:
            if item in string.ascii_letters:    
                output = cipher_dict[item]
                output_text += output
            else:
                output = item
                output_text += output
        return output_text

class PlaintextMessage(Message):
    def __init__(self, text, shift):
        '''
        Initializes a PlaintextMessage object        
        
        text (string): the message's text
        shift (integer): the shift associated with this message

        A PlaintextMessage object inherits from Message and has five attributes:
            self.message_text (string, determined by input text)
            self.valid_words (list, determined using helper function load_words)
            self.shift (integer, determined by input shift)
            self.encrypting_dict (dictionary, built using shift)
            self.message_text_encrypted (string, created using shift)

        Hint: consider using the parent class constructor so less 
        code is repeated
        '''
        Message.__init__(self, text)
        self.shift = shift
        self.encrypting_dict = self.build_shift_dict(self.shift)
        self.message_text_encrypted = self.apply_shift(self.shift)

    def get_shift(self):
        '''
        Used to safely access self.shift outside of the class
        
        Returns: self.shift
        '''
        return self.shift

    def get_encrypting_dict(self):
        '''
        Used to safely access a copy self.encrypting_dict outside of the class
        
        Returns: a COPY of self.encrypting_dict
        '''
        return self.encrypting_dict.copy()

    def get_message_text_encrypted(self):
        '''
        Used to safely access self.message_text_encrypted outside of the class
        
        Returns: self.message_text_encrypted
        '''
        return self.message_text_encrypted

    def change_shift(self, shift):
        '''
        Changes self.shift of the PlaintextMessage and updates other 
        attributes determined by shift (ie. self.encrypting_dict and 
        message_text_encrypted).
        
        shift (integer): the new shift that should be associated with this message.
        0 <= shift < 26

        Returns: nothing
        '''
        self.shift = shift
        self.encrypting_dict = self.build_shift_dict(self.shift)
        self.get_message_text_encrypted = self.apply_shift(self.shift)
        return None

My error:

me = PlaintextMessage("1.hello!!",8)
me.get_message_text_encrypted()

gives me '1.pmttw!!'

mom.change_shift(5)
mom.get_message_text_encrypted()

gives me:
Traceback (most recent call last):

File "", line 1, in
mom.get_message_text_encrypted()

TypeError: 'str' object is not callable. I'm not able to follow why do I've this error.


#2

Try doing this without the paranthesis.

You can access attributes directly in Python. If you include the paranthesis Python thinks you are trying to call a method.


#3

@eveat

Yes, I'm trying to access the method get_message_text_encrypted() and hence the parentheses. While, I know that I can access attributes directly in python, I don't want to do that and want to access attributes using the methods defined in the class.


#4

I got confused because in the change_shift method you use get_message_text_encrypted as if it was an attribute:

self.get_message_text_encrypted = self.apply_shift(self.shift)

I think an attribute of the same name hides a method.


#5

@eveat
Yes. It is an attribute but to avoid accessing the attribute directly, I declare a helper method within the class that is
def get_message_text_encrypted()
which is only used to return the value of that attribute. This is to make me not access the attribute directly as that seems to be the best practice when working with classes.


#6

Are you sure that you want to have an attribute called get_message_text_encrypted?

In the comment inside of change_shift you say you want to update message_text_encrypted, not get_message_text_encrypted.

I am still talking about this line(second to last in the change_shift definition):

Shouldn't that be

self.message_text_encrypted = self.apply_shift(self.shift)

?


#7

@eveat
Thanks. That was the error. Got it. So much time over such a silly error. Dayummm.. Thanks again.


#8

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.