Why does this code print the constructor? - python

class CipherTest:
def __init__(self):
self.shift = 0
self.direction = 'r'
self.text = "Testing"
# Shift to right function
def shift_to_right(self, text, shift):
encrypted_text = ""
for i in range(len(self.text)):
c = self.text[i]
# Encrypt upper case
if (c == ' '):
encrypted_text += ' '
elif (c.isupper()):
encrypted_text += chr((ord(c) + self.shift - 65) % 26 + 65)
# Encrypt lower case
else:
encrypted_text += chr((ord(c) + self.shift - 97) % 26 + 97)
return encrypted_text
# Shift to left function
def shift_to_left(self, text, shift):
encrypted_text = ""
for i in range(len(self.text)):
c = self.text[i]
# Encrypt upper case
if (c == ' '):
encrypted_text += ' '
elif (c.isupper()):
encrypted_text += chr((ord(c) - self.shift - 65) % 26 + 65)
# Encrypt lower case
else:
encrypted_text += chr((ord(c) - self.shift - 97) % 26 + 97)
return encrypted_text
if __name__ == "__main__":
user_text = str(input())
user_shift = int(input())
user_direction = str(input().lower()) # user inputs
Cipher_Message = CipherTest() # create an instance of the class
if user_direction == 'l': # picking left or right
print(CipherTest.shift_to_left(Cipher_Message, user_text, user_shift))
if user_direction == 'r':
print(CipherTest.shift_to_right(Cipher_Message, user_text, user_shift))
Am I calling the functions within my class incorrectly? Currently no matter the input, it prints "Testing". I'm new to classes but I believe that in the last couple lines, I am calling the new instance, telling it which method to perform, then feeding it the variables it needs to successfully run. From there it should be printing the 'encrypted_text' that is returned using my methods.

The issue is that CipherTest have attributes direction, shift and text but you also have local variables that you pass as parameter which you don't use
So your method keep using Testing and 0, that returns Testing so
The easiest, is to remove the attributs, so the class instance can do both direction, and in fact the method can be static (meaning no instance needed, only the class)
class CipherTest:
#staticmethod
def shift_to_right(text, shift):
encrypted_text = ""
for c in text:
if c == ' ':
encrypted_text += ' '
elif c.isupper():
encrypted_text += chr((ord(c) + shift - 65) % 26 + 65)
else:
encrypted_text += chr((ord(c) + shift - 97) % 26 + 97)
return encrypted_text
if __name__ == "__main__":
user_text = "hey how are you"
user_shift = 2
user_direction = "l"
if user_direction == 'l':
enc = CipherTest.shift_to_left(user_text, user_shift)
print(enc) # encrypted
print(CipherTest.shift_to_right(enc, user_shift)) # back to plain
elif user_direction == 'r':
enc = CipherTest.shift_to_right(user_text, user_shift)
print(enc) # encrypted
print(CipherTest.shift_to_left(enc, user_shift)) # back to plain
Another example, so you understand better, is to put the shift as class attribut, in order to use ise, we need to save in the class with self.shift = shift then we can use one cipher instance, with it's shift, to do both operation (also see how we really call a instance method)
class CipherTest:
def __init__(self, shift: int):
self.shift = shift
def shift_to_right(self, text):
encrypted_text = ""
for c in text:
if c == ' ':
encrypted_text += ' '
elif c.isupper():
encrypted_text += chr((ord(c) + self.shift - 65) % 26 + 65)
else:
encrypted_text += chr((ord(c) + self.shift - 97) % 26 + 97)
return encrypted_text
if __name__ == "__main__":
user_text = "hey how are you"
user_shift = 2
user_direction = "l"
cipher = CipherTest(user_shift)
if user_direction == 'l':
enc = cipher.shift_to_left(user_text)
print(enc) # encrypted
print(cipher.shift_to_right(enc)) # back to plain
elif user_direction == 'r':
enc = cipher.shift_to_right(user_text)
print(enc) # encrypted
print(cipher.shift_to_left(enc)) # back to plain

You're passing the class in as a parameter to your function.
CipherTest.shift_to_left(Cipher_Message, user_text, user_shift)
When you define a class method in python, you include self as a parameter but don't actually pass it when calling the function. It's interpreting Cipher_Message (the instance) as what you intended to put in as user_text.
Fix this by simply removing Cipher_Message from your parameters:
CipherTest.shift_to_left(user_text, user_shift)
You're not done yet. You're attempting to call a non-static method on the Class, instead of the instance. This means you're telling the 'blueprint' to shift your encryption as opposed to your actual test class. Fix this by changing the class to the instance:
Cipher_Message.shift_to_left(user_text, user_shift)
A nonbreaking bug you also have is that your functions seem to be taking parameters that you aren't using.
def shift_to_right(self, text, shift): takes two parameters - text & shift, yet you use the class's fields instead of those.
Simply removing them from the definitions and calls will remedy this.
def shift_to_right(self):
.shift_to_right()

Within your class methods, you are accepting parameters of text and shift, but the code of the methods never references those parameters.
The methods only reference the instance variables self.text and self.shift, which are attributes of the instance Cipher_Message, assigned during the execution of __init__ when the new instance was created
Two options:
Update the methods to use the parameters instead of the instance attributes.
Update __init__ to take text and shift as parameters, and assign the instance attributes based on the parameters that were passed in.

Ignoring how the user input is handled, this can be greatly simplified.
class CipherTest:
#staticmethod
def shift_to_left(text, shift):
return CipherTest.shift(text, -shift)
#staticmethod
def shift_to_right(text, shift):
return CipherTest.shift(text, shift)
#staticmethod
def shift(text, shift):
result = ''
for c in text:
if c.isupper():
c = chr((ord(c) + shift - 65) % 26 + 65)
elif c.islower():
c = chr((ord(c) + shift - 97) % 26 + 97)
# note that whitespace is neither upper nor lower so just append unchanged
result += c
return result
print(CipherTest.shift_to_left(CipherTest.shift_to_right('Hello world', 3), 3))
...which keeps the interface similar. Alternatively:
print(CipherTest.shift(CipherTest.shift('Hello world', 3), -3))
Output:
Hello world

Related

Encoding a password with special characters

I am in an into to Programming class and the assignment is in part to shift the letters of a valid password by +1 if it is a letter or number and -1 if it is a !##$ special character. I have most of it working with my below code but I always get the wrong output for the special characters. If I use to the code of anything high like 128 then I get the wrong symbol.
I am using the code from an encryption program from the other week and slowly changing things but I feel like this is too involved for something simple
If I enter the password UMA#augusta2020 I need to get the output VNB?bvhvtub3131 but I either end up with a space, b, or wrong symbol when I change the code input between 26,64,96,128, etc.
I have updated the code to fix small errors
def main():
print("This program validates and encrypts a password")
print()
main()
# The Encryption Function
def cipher_encrypt(plain_text):
encrypted = ""
for c in plain_text:
if c.isupper(): #check if it's an uppercase character
c_index = ord(c) - ord('A')
# shift the current character by key positions
c_shifted = (c_index + 1) % 26 + ord('A')
c_new = chr(c_shifted)
encrypted += c_new
elif c.islower(): #check if its a lowecase character
c_index = ord(c) - ord('a')
c_shifted = (c_index + 1) % 26 + ord('a')
c_new = chr(c_shifted)
encrypted += c_new
elif c.isdigit():
# if it's a number,shift its value
c_new = (int(c) + 1) % 10
encrypted += str(c_new)
else:
# if its neither alphabetical nor a number, -1
c_shifted = (c_index - 1) % 128 + ord('a')
c_new = chr(c_shifted)
encrypted += c_new
return encrypted
plain_text =input("Enter your Password: ")
print()
ciphertext = cipher_encrypt(plain_text)
print()
print("Your Password: ", plain_text)
print()
print("Your password is valid and encrypted it is: ", ciphertext)
print()
Here is a much cleaner approach:
def cipher_encrypt(plain_text):
alpha = 'abcdefghijklmnopqrstuvwxyz'
digits = '0123456789'
puncs = '!"#$%&\'()*+,-./:;<=>?#[\\]^_`{|}~'
encrypted_text = ''
for c in plain_text:
if c.lower() in alpha:
c_index = alpha.index(c.lower())
e_index = (c_index + 1)%len(alpha)
if c == c.lower():
encrypted_text += alpha[e_index]
else:
encrypted_text += alpha[e_index].upper()
elif c in digits:
c_index = digits.index(c)
e_index = (c_index + 1)%len(digits)
encrypted_text += digits[e_index]
else:
c_index = puncs.index(c)
e_index = (c_index +len(puncs) - 1)%len(puncs)
encrypted_text += puncs[e_index]
return encrypted_text
In this approach I deal with each caharcter in plain text by:
Determining idf the char is part of alpha by making use of the c.lower() function to isolate alpha chars and then find an index modulo the len of alpha. Then I determine if I need an uppercase char or lower case in the encrypted text.
I use the same modulo approach for digits, but don't have to worry about upper and lower, finally
I find the index for the punctuations found in the plain_text

Caesar Cypher Python using RAD software

So currently I am working on a Caesar cypher app, my code looks like this (coded by RAD software https://anvil.works/)
The problem is that I don't get any output when I try to run the program and I think that it could be self.text_area_2 = cipher_encrypt(plain_text, key). I'm not sure if I'm using the correct statement to display the output correctly.
What statement should I use instead? Am I doing the right thing?
from ._anvil_designer import Form2Template
from anvil import *
class Form2(Form2Template):
def __init__(self, **properties):
# Set Form properties and Data Bindings.
self.init_components(**properties)
def button_1_click(self, **event_args):
def cipher_encrypt(plain_text, key):
plain_text = self.text_area_1.text
key = self.text_box_1.text
encrypted = ""
for c in plain_text:
if c.isupper(): #check if it's an uppercase character
c_index = ord(c) - ord('A')
# shift the current character by key positions
c_shifted = (c_index + key) % 26 + ord('A')
c_new = chr(c_shifted)
encrypted += c_new
elif c.islower(): #check if its a lowecase character
# subtract the unicode of 'a' to get index in [0-25) range
c_index = ord(c) - ord('a')
c_shifted = (c_index + key) % 26 + ord('a')
c_new = chr(c_shifted)
encrypted += c_new
elif c.isdigit():
# if it's a number,shift its actual value
c_new = (int(c) + key) % 10
encrypted += str(c_new)
else:
# if its neither alphabetical nor a number, just leave it like that
encrypted += c
return encrypted
self.text_area_2 = cipher_encrypt(plain_text, key)

Interactive rot13 cipher

I have a code and I'm having trouble making it interactive.
Here's the problem:
"""Write a function called rot13 that uses the Caesar cipher to encrypt a message. The Caesar cipher works like a substitution cipher but each character is replaced by the character 13 characters to “its right” in the alphabet. So for example the letter “a” becomes the letter “n”. If a letter is past the middle of the alphabet then the counting wraps around to the letter “a” again, so “n” becomes “a”, “o” becomes “b” and so on. Hint: Whenever you talk about things wrapping around its a good idea to think of modulo arithmetic (using the remainder operator)."""
Here's the code to this problem:
def rot13(mess):
alphabet = 'abcdefghijklmnopqrstuvwxyz'
encrypted = ''
for char in mess:
if char == ' ':
encrypted = encrypted + ' '
else:
rotated_index = alphabet.index(char) + 13
if rotated_index < 26:
encrypted = encrypted + alphabet[rotated_index]
else:
encrypted = encrypted + alphabet[rotated_index % 26]
return encrypted
def main():
print(rot13('abcde'))
print(rot13('nopqr'))
print(rot13(rot13('since rot thirteen is symmetric you should see this message')))
if __name__ == "__main__":
main()
I want to make it interactive where you can input any message and you can rotate the letters however many times as you want. Here is my attempt. I understand you'd need two parameters to pass, but I'm clueless as to how to replace a few items.
Here's my attempt:
def rot13(mess, char):
alphabet = 'abcdefghijklmnopqrstuvwxyz'
encrypted = ''
for char in mess:
if char == ' ':
encrypted = encrypted + ' '
else:
rotated_index = alphabet.index(char) + mess
if rotated_index < 26:
encrypted = encrypted + alphabet[rotated_index]
else:
encrypted = encrypted + alphabet[rotated_index % 26]
return encrypted
def main():
messy_shit = input("Rotate by: ")
the_message = input("Type a message")
print(rot13(the_message, messy_shit))
if __name__ == "__main__":
main()
I don't know where my input should be taking place in the function. I have a feeling it could be encrypted?
This is probably what you're looking for. It rotates the message by the messy_shit input.
def rot13(mess, rotate_by):
alphabet = 'abcdefghijklmnopqrstuvwxyz'
encrypted = ''
for char in mess:
if char == ' ':
encrypted = encrypted + ' '
else:
rotated_index = alphabet.index(char) + int(rotate_by)
if rotated_index < 26:
encrypted = encrypted + alphabet[rotated_index]
else:
encrypted = encrypted + alphabet[rotated_index % 26]
return encrypted
def main():
messy_shit = input("Rotate by: ")
the_message = input("Type a message")
print(rot13(the_message, messy_shit))
def rot(message, rotate_by):
'''
Creates a string as the result of rotating the given string
by the given number of characters to rotate by
Args:
message: a string to be rotated by rotate_by characters
rotate_by: a non-negative integer that represents the number
of characters to rotate message by
Returns:
A string representing the given string (message) rotated by
(rotate_by) characters. For example:
rot('hello', 13) returns 'uryyb'
'''
assert isinstance(rotate_by, int) == True
assert (rotate_by >= 0) == True
alphabet = 'abcdefghijklmnopqrstuvwxyz'
rotated_message = []
for char in message:
if char == ' ':
rotated_message.append(char)
else:
rotated_index = alphabet.index(char) + rotate_by
if rotated_index < 26:
rotated_message.append(alphabet[rotated_index])
else:
rotated_message.append(alphabet[rotated_index % 26])
return ''.join(rotated_message)
if __name__ == '__main__':
while True:
# Get user input necessary for executing the rot function
message = input("Enter message here: ")
rotate_by = input("Enter your rotation number: ")
# Ensure the user's input is valid
if not rotate_by.isdigit():
print("Invalid! Expected a non-negative integer to rotate by")
continue
rotated_message = rot(message, int(rotate_by))
print("rot-ified message:", rotated_message)
# Allow the user to decide if they want to continue
run_again = input("Continue? [y/n]: ").lower()
while run_again != 'y' and run_again != 'n':
print("Invalid! Expected 'y' or 'n'")
run_again = input("Continue? [y/n]: ")
if run_again == 'n':
break
Note: It's more efficient to create a list of characters then join them to produce a string instead of using string = string + char. See Method 6 here and the Python docs here. Also, be aware that our rot function only works for lowercase letters of the alphabet. It'll break if you try to rot a message with uppercase characters or any character that isn't in alphabet.

Python - How can I strip clear my string of trailing spaces when I am using recursion?

I am fairly new to programming, and python is my first language. I am learning on my own so maybe I am missing something. Sorry, if I could not word my problem properly.
I have written a function that accepts any number between 0-99 and converts it into mandarin based on a dictionary. Here is the function:
trans = {'0':'ling', '1':'yi', '2':'er', '3':'san', '4': 'si','5':'wu', '6':'liu', '7':'qi', '8':'ba', '9':'jiu', '10': 'shi'}
def convert_to_mandarin(us_num, flag = True):
'''
us_num, a string representing a US number 0 to 99
returns the string mandarin representation of us_num
'''
if len(us_num) == 1:
if us_num != '0':
return trans[us_num]
elif flag:
return trans[us_num]
else:
return '\b'
elif len(us_num) > 1:
if us_num[0] == '1':
return trans['10'] +' '+ convert_to_mandarin(us_num[1:], False)
else:
return trans[us_num[0]] + ' ' + trans['10'] + ' ' + convert_to_mandarin(us_num[1:], False)
else:
return convert_to_mandarin(us_num[:-1], False) + trans[us_num[-1]]
But for number ending with 0, the string is being returned with a trailing space. For example if n = convert_to_mandarin('10') then it returns n = 'shi '
but I want it to be n = 'shi'
So how can I get rid of the trailing space?
you insert a space and try to delete it using backspace. But that is no good in files or debug.
Don't return anything where you returned the backspace, and strip the added strings afterwards to get rid of trailing spaces using str.rstrip:
trans = {'0':'ling', '1':'yi', '2':'er', '3':'san', '4': 'si','5':'wu', '6':'liu', '7':'qi', '8':'ba', '9':'jiu', '10': 'shi'}
def convert_to_mandarin(us_num, flag = True):
'''
us_num, a string representing a US number 0 to 99
returns the string mandarin representation of us_num
'''
if len(us_num) == 1:
if us_num != '0':
return trans[us_num]
elif flag:
return trans[us_num]
else:
return ''
elif len(us_num) > 1:
if us_num[0] == '1':
return (trans['10'] +' '+ convert_to_mandarin(us_num[1:], False)).rstrip()
else:
return (trans[us_num[0]] + ' ' + trans['10'] + ' ' + convert_to_mandarin(us_num[1:], False)).rstrip()
else:
return convert_to_mandarin(us_num[:-1], False) + trans[us_num[-1]]
(sometimes a post-correction is easier than trying to figure out how to avoid the problem, specially with recursion. Note that rstrip returns the same string if no replacement done, so the overhead is negligeable)

Python - Concatenating

I'm going crazy with the following code which should be really easy but doesn't work :/
class Triangulo_String:
_string = ''
_iteraciones = 0
_string_a_repetir = ''
def __init__(self, string_a_repetir, iteraciones):
self._string_a_repetir = string_a_repetir
self._iteraciones = iteraciones
def concatenar(self):
for i in range(0, self._iteraciones, 1):
self._string = self._string_a_repetir + self._string + '\n'
I'm initializing _iteraciones to 3 and _string_a_repetir to '*'
And the output is just:
***
When I'm expecting:
*
**
***
I've debugged it and when doing the concatenating it just concatenates the self._string_a_repetir, not the _string nor the line break.
Such an easy thing is driving me crazy ._.
The relevant bit is in this part:
for i in range(0, self._iteraciones, 1):
self._string = self._string_a_repetir + self._string + '\n'
Let’s go through the iterations one by one:
# Initially
_string = ''
_string_a_repetir = '*'
_iteraciones = 3
# i = 0
_string = _string_a_repetir + _string + '\n'
= '*' + '' + '\n'
= '*\n'
# i = 1
_string = _string_a_repetir + _string + '\n'
= '*' + '*\n' + '\n'
= '**\n\n'
# i = 2
_string = _string_a_repetir + _string + '\n'
= '*' + '**\n\n' + '\n'
= '***\n\n\n'
As you can see, this is totally expected to happen, since you never repeat that character more than once per line. And you are also incorrectly concatenating the previous string with the new string (placing it in between the current line’s text and its line break).
What you are looking for is something like this:
for i in range(0, self._iteraciones, 1):
self._string = self._string + (self._string_a_repetir * (i + 1)) + '\n'
The string * number works to repeat the string for number times.
As a general note, you should not use those class members that way:
class Triangulo_String:
_string = ''
_iteraciones = 0
_string_a_repetir = ''
This will create those members as class variables, which are shared across all its instances. This is not directly a problem if you never change the class members but it could cause confusion later. You should instead initialize all instance attributes inside the __init__:
class Triangulo_String:
def __init__(self, string_a_repetir, iteraciones):
self._string = ''
self._string_a_repetir = string_a_repetir
self._iteraciones = iteraciones

Categories