Nesting a function inside itself (i'm desperate) - python

Mentally exhausted.
An explanation just for context, dont actually need help with hashes:
I'm trying to make a python script that can bruteforce a hashed string or password (learning only, i'm sure there are tenter code herehousands out there).
The goal is making a function that can try all the possible combinations of different letters, starting from one character (a, b... y, z) and then start trying with one more character (aa, ab... zy, zz then aaa, aab... zzy, zzz) indefinetly until it finds a match.
First, it asks you for a string (aaaa for example) then it hashes the string, and then try to bruteforce that hash with the function, and finally the function returns the string again when it finds a match.
PASSWORD_INPUT = input("Password string input: ")
PASSWORD_HASH = encrypt_password(PASSWORD_INPUT) # This returns the clean hash
found_password = old_decrypt() # This is the function below
print(found_password)
I managed to do this chunk of ugly ass code:
built_password = ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
def old_decrypt():
global built_password
# First letter
for a in range(len(characters)): # Characters is a list with the abecedary
built_password[0] = characters[a]
if test_password(pretty(built_password)): # This returns True if it matches
return pretty(built_password)
# Second letter
for b in range(len(characters)):
built_password[1] = characters[b]
if test_password(pretty(built_password)):
return pretty(built_password)
# Third letter
for c in range(len(characters)):
built_password[2] = characters[c]
if test_password(pretty(built_password)):
return pretty(built_password)
# Fourth letter
for d in range(len(characters)):
built_password[3] = characters[d]
if test_password(pretty(built_password)):
return pretty(built_password)
The problem of this is that it only works with 4 letters strings.
As you can see, it's almost the exact same loop for every letter, so i thought "Hey i can make this a single function"... After obsessively trying everything that came to my mind for 3 whole days i came with this:
# THIS WORKS
def decrypt(letters_amount_int):
global built_password
for function_num in range(letters_amount_int):
for letter in range(len(characters)):
built_password[function_num] = characters[letter]
if letters_amount_int >= 1:
decrypt(letters_amount_int - 1)
if test_password(pretty(built_password)):
return pretty(built_password)
# START
n = 1
while True:
returned = decrypt(n)
# If it returns a string it gets printed, else trying to print None raises TypeError
try:
print("Found hash for: " + returned)
break
except TypeError:
n += 1
Function gets a "1", tries with 1 letter and if it doesnt return anything it gets a "2" and then tries with 2.
It works, but for some reason it makes a ridiculous number of unnecessary loops that takes exponentially more and more time, i've been smashing my head and came to the conclussion that i'm not understanding something about python's internal functioning.
Can someone please drop some light on this? Thanks
In case of needed these are the other functions:
def encrypt_password(password_str):
return hashlib.sha256(password_str.encode()).hexdigest()
def test_password(password_to_test_str):
global PASSWORD_HASH
if PASSWORD_HASH == encrypt_password(password_to_test_str):
return True
characters = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D',
'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z']

Recursion in this case gives a very elegant solution:
def add_char(s, limit):
if len(s) == limit:
yield s
else:
for c in characters:
yield from add_char(s + c, limit)
def generate_all_passwords_up_to_length(maxlen):
for i in range(1, maxlen + 1):
yield from add_char("", i)
for password in generate_all_passwords_up_to_length(5):
test_password(password)

Maybe you could try something like this. Inspired by Multiple permutations, including duplicates
Itertools has a cartesian product generator, which is related to permutation.
import itertools
def decrypt(characters, num_chars):
for test_list in itertools.product(characters, repeat=num_chars):
test_str = ''.join(test_list)
if test_password(test_str):
return test_str
return None
for i in range(min_chars, max_chars+1):
result = decrypt(characters, i)
if result:
print(f'Match found: {result}')
If you run this code with characters, min_chars, max_chars = (characters, 1, 3) and print test_str at each step, you'll get:
0
1
2
00
01
02
10
11
12
20
21
22
or will stop earlier if a match is found. I recommend you look up a recursive, pure python implementation of the the cartesian product function if you want to learn more. However, I'd suspect the cartesian product generator will be faster than a recursive solution.
Note that itertools.product() is a generator, so it's generating each value on demand, and writing it this way allows you to find a match for shorter strings faster than longer ones. But the time it takes this brute force algorithm should indeed increase exponentially with the length of the true password.
Hope this helps.

Related

Both codes work(caesar cipher): but one code rearranges the output

Beginner python programmer here. Before I knew about using .index(), i used a work around. Whilst it did work something peculiar happened. The output string was re-arranged and i don't know why.
Here is my code:
alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
text = input("Type your message:\n").lower()
shift = int(input("Type the shift number:\n"))
#input for text = "code" integer for shift = 5
#First attempt
for index, price in enumerate(alphabet):
new_index = shift + index
for loop in text:
if loop == price:
print(alphabet[new_index])
#Second attempt using .index
for letter in text:
position = alphabet.index(letter)
new_index = position + shift
print(alphabet[new_index])
Here are the outputs
output for first code = hijt
output for second code = htij
Your first code prints the word with the letters rearranged in alphabetical order (before using the cipher). You go through the alphabet in your enumerate, a-z, and you look for each letter in your word. For example, if your word was 'ba', with a shift of one, it should output 'cb' - but it outputs 'bc'. It is because your loop looks for 'a's and prints the converted values out before doing so for 'b's.
Your second is correct.
Note: I have no idea why your sample output is on a single line - print generally adds a newline, so each letter would have been on a separate line. Also, you should realize that your code doesn't work when the new letter goes past 'z' - it has an index out of range error.

Trying to convert user define function result into list in python

#Password Generator Project
import random
letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
numbers = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
symbols = ['!', '#', '$', '%', '&', '(', ')', '*', '+']
print("Welcome to the PyPassword Generator!")
nr_letters= int(input("How many letters would you like in your password?\n"))
nr_symbols = int(input(f"How many symbols would you like?\n"))
nr_numbers = int(input(f"How many numbers would you like?\n"))
#Eazy Level - Order not randomised:
#e.g. 4 letter, 2 symbol, 2 number = JduE&!91
def my_function():
for i in range(1,nr_letters+1):
variable=random.choice(letters)
print(variable, end='')
for j in range(1,nr_symbols+1):
variable1=random.choice(symbols)
print(variable1, end='')
for k in range(1,nr_numbers+1):
variable2=random.choice(numbers)
print(variable2, end='')
#Hard Level - Order of characters randomised:
#e.g. 4 letter, 2 symbol, 2 number = g^2jk8&P
#my_function()
function_to_list=my_function()
print[(function_to_list)]
shuffle_my_function=random.shuffle(function_to_list)
print(shuffle_my_function)
This is a kind of personal project here my task is to generate a password. On the easy level just print the password sequence wise whereas on the hard level I want my password to be shuffled.
My easy level code is working perfectly but on the hard level, I want to shuffle the result of the easy level for that I thought that if I define a function of the easy part and then somehow convert that function into the list I can easily use shuffle function . so please help me. please try to do give a solution in my way of thinking and then please suggest your way of doing it
This is a common beginner problem. When you print something, it's there on the screen, but only as text. And the program can't see that text. You have to do something to the variables to keep track of them as you go. In this case, you need to append them to a list. Not only that, but you need to return the list to the caller, so that function_to_list = my_function() assigns something other than None to function_to_list:
def my_function():
list_of_characters = []
for i in range(nr_letters):
list_of_characters.append(random.choice(letters))
for j in range(nr_symbols):
list_of_characters.append(random.choice(symbols))
for k in range(nr_numbers):
list_of_characters.append(random.choice(numbers))
return list_of_characters
Notice that I took out the print statements. That's because a function should do only one thing and do it well. You can print your list and your password as soon as you get them back:
list_from_function = my_function()
print(list_from_function)
To print the list as a single string, join the letters it contains with the emtpy string:
print(''.join(list_from_function))
You can shuffle the result, or do whatever you want:
random.shuffle(list_from_function)
print(list_from_function)
Keep in mind that shuffle operates in place and returns None. That means that if you try to print its return value, you get nothing.
You don't need to use for loop. You can pass argument to random.choices() indicating how many items you want.
import random
import string
letters = string.ascii_letters
numbers = string.digits
symbols = ['!', '#', '$', '%', '&', '(', ')', '*', '+']
# For demo, I hardcoded the numbers
nr_letters = 4
nr_symbols = 5
nr_numbers = 3
# create a list from randomly choosen characters
password_characters = random.choices(letters, k = nr_letters) \
+ random.choices(symbols, k = nr_symbols) \
+ random.choices(numbers, k = nr_numbers)
# shuffle the list:
random.shuffle(password_characters)
# convert the list to string
password = ''.join(password_characters)
Output:
>>> print(password)
>>> &J0*4oR!I3$!

Pangram detection

beginner here--
Given a string, my code must detect whether or not it is a pangram. Return True if it is, False if not.It should ignore numbers and punctuation.
When given "ABCD45EFGH,IJK,LMNOPQR56STUVW3XYZ" it returns none and when given "This isn't a pangram! is not a pangram." it returns True when the answer should be False.
This isn't a pangram! is not a pangram. What am I not seeing?
import string
def is_pangram(s):
singlechar = set(s)
list = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
for index, item in enumerate(singlechar):
if item in list:
list.remove(item)
if list:
return True
break
if not list:
return False
Sets are a great way to check whether something belongs in two collections with their intersection or doesn't belong in one of the two with their difference.
In your case, if the intersection between the set of the letters in your phrase and the letters a-z is of length 26, it is a pangram.
from string import ascii_lowercase
def is_pangram(s):
return len(set(s.lower()).intersection(ascii_lowercase)) == 26
You could have just continued to use sets and their method .difference to find out if there were more characters in the set of all characters or there were no differences (before that you would need to strip the string from punctuation (and whitespace) and make it lowercase (done by .lower and .translate and .maketrans methods of strings)):
import string
def is_pangram(s):
input_set = set(s.lower().translate(
str.maketrans('', '', f'{string.punctuation} ')))
check_set = set(string.ascii_lowercase)
return not check_set.difference(input_set)
value1 = 'The quick brown fox jumps over a lazy dog!'
print(is_pangram(value1))
# True
value2 = 'This isn\'t a pangram! is not a pangram'
print(is_pangram(value2))
# False
If you want to still do it with a list:
def is_pangram(s):
input_set = set(s.lower().translate(
str.maketrans('', '', f'{string.punctuation} ')))
lst = list(string.ascii_lowercase)
for item in input_set:
if item in lst:
lst.remove(item)
if not lst:
return True
return False

How do I return a value within a loop within a function?

I have this almost figured it out but there is one thing. Basically I want to return a string without a vowel (a common challenge I guess). This is similar to other challenges on CodeWars I have done, still uncompleted due to this. I have a for loop within a function. I call the function to return value.
For some reason, I'm returning empty or rather "None", yet I get the result I wanted by printing. On the same line and indenting.
This is for a Codewar challenge, so I need to return values instead of , printing, logging (I know). I asked for a friend, hours of researching but nothing could help me.
def disemvowel(string):
#aeiou
vowel = ['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U']
aList = list(string) #'' to [...]
for x in aList:
for y in vowel:
if x == y:
#print(x)
aList.remove(x)
print(''.join(aList)) # "Ths wbst s fr lsrs LL!"
return(''.join(aList)) # Nothing shows up here...
I expect the output of "Ths wbst s fr lsrs LL!" by returning but I get None.
https://www.codewars.com/kata/52fba66badcd10859f00097e/train/python
Source ^
To remove vowels from strings, the quickest way would be to use str.replace.
def remove_vowels(msg):
vowels = ['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U']
for vowel in vowels:
msg = msg.replace(vowel, '')
return msg
Use a list comprehension:
def remove_vowels(msg):
return ''.join(c for c in msg if c.lower() not in {'a', 'e', 'i', 'o', 'u'})
Examples:
>>> remove_vowels("Lorem ipsum dolor sit amet.")
'Lrm psm dlr st mt.'
>> remove_vowels("This is it")
'Ths s t'
>>> remove_vowels("This website is for losers LOL!")
'Ths wbst s fr lsrs LL!'

Python Function That Receives Letter, Returns (0-Based) Numerical Position Within Alphabet

I'm trying to create a Python function that receives a letter (a string with only one alphabetic character) and returns the 0-based numerical position of that letter in the alphabet. It should not be case-sensitive, and I can't use import.
So entering "a" should return
0
Entering "A" should also return
0
Entering "O" should return
14
And so on.
I had noticed this question but the top answer uses import and the second answer doesn't make any sense to me / doesn't work. I tried to apply the second answer like this:
letter = input("enter a letter")
def alphabet_position(letter):
return ord(letter) - 97
print((alphabet_position)(letter))
but I got a TypeError:
TypeError: ord() expected a character, but string of length 2 found
Just like the asker in the question that I linked, I'm also trying to send the characters "x" amount of steps back in the alphabet, but in order to do that I need to create this helper function first.
I'm thinking there must be a way to store the letters in two separate lists, one lower-case and one upper-case, and then see if the string that the user entered matches one of the items in that list? Then once we find the match, we return it's (0-based) numerical position?
letter = input("enter a letter")
def alphabet_position(letter):
position = 0
#letter_position = index value that matches input
lower_case_list ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
'y', 'z']
upper_case_list ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y','Z']
#if letter is in lower_case_list, return it's 0-based numerical position.
#else, if letter is in upper_case_list, return it's 0-based numerical position.
#else, print("Enter a valid letter")
return letter_position
Please help if you have any suggestions. Thank you.
It's probably simpler to just convert from uppercase or lowercase to specifically lowercase with the .lower() method, and use the built in list of letters (string.ascii_lowercase). You can find the index of a list's element by using the .index() method.
import string
letter = input('enter a letter: ')
def alphabet_position(letter):
letter = letter.lower()
return list(string.ascii_lowercase).index(letter)
print(alphabet_position(letter))
When you called alphabet_position, it is expecting an argument so you need to do func_name(arg) format.
Another way you could do this is to use dictionary comprehension to create a dict of letter-position pairs, like so:
from string import lowercase as l
alphabet_lookup = {letter:pos for letter,pos in zip(l, range(len(l)))}
and then
f = lambda letter: alphabet_lookup[letter.lower()]
is the desired function.
I suggest using a dictionary. It might be a large amount of code to do something relatively simple, but I find it easier to make sense of it this way (and if you are new to python it will help you learn). If you google python dictionaries you can learn lots more about them, but here is the basic concept:
Code for python version 3.X:
def alphabet_position(letter):
alphabet_pos = {'A':0, 'a':0, 'B':1, 'b':1}
pos = alphabet_pos[letter]
return pos
letter = input('Enter a letter: ')
print(alphabet_position(letter))
Code for python version 2.7:
def alphabet_position(letter):
alphabet_pos = {'A':0, 'a':0, 'B':1, 'b':1}
pos = alphabet_pos[letter]
return pos
letter = raw_input('Enter a letter: ')
print alphabet_position(letter)
If you run that, it will print 1 because it searches through the alpahbet_pos dictionary and finds the value that corresponds to the entry entitled 'B'. Notice that you can have multiple entries with the same value, so you can do uppercase and lowercase in the same dictionary. I only did letters A and B for the sake of time, so you can fill out the rest yourself ;)
I once had to enter every element on the periodic table and their corresponding atomic mass, and that took forever (felt much longer than it actually was).

Categories

Resources