Error in implementing inheritance


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

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

gives me '1.pmttw!!'


gives me:
Traceback (most recent call last):

File "", line 1, in

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


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.



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.


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.


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.


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)



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


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