Python: Assertion error when converting a string to binary - python

I am getting AssertionError when testing some basic functions in my python program. This is the scenario:
I have written a function that converts a single letter into binary from ASCII:
def ascii8Bin(letter):
conv = ord(letter)
return '{0:08b}'.format(conv)
Next there's a function that uses the previous function to convert all the letters in a word/sentence into binary:
def transferBin(string):
l = list(string)
for c in l:
print ascii8Bin(c)
Now when I try to assert this function like this:
def test():
assert transferBin('w') == '01110111'
print "You test yielded no errors"
print test()
It throws an AssertionError. Now I looked up the binary alphabet and tripple checked: w in binary is definitely 01110111. I tried calling just the transferBin('w') and it yielded 01110111 like it should.
I am genuinely interested in why the assertion fails. Any insight is very much appreciated.

Your function doesn't return anything. You use print, which writes text to stdout, but you are not testing for that.
As such, transferBin() returns None, the default return value for functions without an explicit return statement.
You'll have to collect the results of each ascii8Bin() result into a list and join the results into a string:
def transferBin(string):
results = [ascii8Bin(c) for c in l]
return '\n'.join(results)
This'll use newlines to separate the result for each character; for your single character 'w' string that'll give you the expected string.
Note that you don't need to turn l in to a list; you can iterate over strings directly; you'll get individual characters either way.

You need to return, you are comparing to None not the string. python will return None from a function if you don't specify a return value.
def transferBin(s):
l = list(s)
for c in l:
return ascii8Bin(c) # return
You are basically doing assert None == '01110111', also if you are only going to have single character string simply return ascii8Bin(string), having a loop with a return like in your code will return after the first iteration so the loop is redundant.
If you actually have multiple characters just use join and just iterate over the string you don't need to call list on it to iterate over a string:
def transferBin(s):
return "".join(ascii8Bin(ch) for ch in s)
You can also just do it all in your transferBin function:
def transferBin(s):
return "".join('{0:08b}'.format(ord(ch)) for ch in s)

Related

Using recursion to concatenate ONLY the letters of an input string into a single output string

"Given a string of both letters and special characters/numbers, use recursion to concatenate the letters into a single string and return it."
My code is below, I'm still learning recursion and am stuck in trying to trace it. I tried a bunch of different lines in this code but idk how to fix what I do have so far:
def decoder(encryptedStr):
if len(encryptedStr) != 0:
if encryptedStr[0].isalpha() == True:
decoded = encryptedStr[0]
decoded.join(decoder(encryptedStr[1:]))
print(decoded)
else:
decoder(encryptedStr[1:])
I haven't had it return anything yet because I'm struggling with the part where I have to join the new letters to the output string. Instead of .join I also tried:
decoded += decoder(encryptedStr[1:])
but it doesn't work bc Nonetype??
Your main issue is that you didnt return, but there are some issues with your approach that make this more complex than need-be.
Think tail-first when doing recursion- What is your end condition, and how do you decide to continue. Typically with this kind of method you do something like, 1) process a single value in the list, 2) let the recursive method handle the rest of it, 3) combine the results.
An easy indicator of the tail-first return here would be to return nothing if the string is empty:
def decoder(encryptedStr):
if len(encryptedStr) == 0:
return ""
...
Now in each run we want to operate on one letter and pass the rest to a recursive call. Ignoring the special character requirement, you'd get something like this:
def decoder(encryptedStr):
if len(encryptedStr) == 0:
return ""
first = encryptedStr[0]
rest = decoder(encryptedStr[1:])
return first + rest
Now we can handle the special case where we want to omit letters.
def decoder(encryptedStr):
if len(encryptedStr) == 0:
return ""
first = encryptedStr[0]
rest = decoder(encryptedStr[1:])
if not first.isalpha():
first = ""
return first + rest
And that's all there is to it!
Bonus for some refactoring:
def clean(letter):
return letter if letter.isalpha() else ""
def decoder(encrypted):
if len(encrypted) == 0:
return ""
return clean(encrypted[0]) + decoder(encrypted[1:])
There's a bunch of problems here:
I don't think join does what you want it to do in that case. If you want to add some strings together simply use +=. join would insert decoded character between whatever decoder(encryptedStr[1:]) returns.
You don't have a case for len(encryptedStr) == 0, so it returns default value of None. That's why you cannot append it's results to decoded.
Return immediately if there is nothing to do. Otherwise take the first letter if it matches the condition and add the result of the recursive call (where the parameter is the current encrypted string without the first character).
def decoder(encrypted):
if not encrypted:
return ''
decrypted = encrypted[0] if encrypted[0].isalpha() else ''
return decrypted + decoder(encrypted[1:])
print(decoder('Abc123rtZ5'))
The result is AbcrtZ.
Bonus info (as #JonSG mentioned in the comments):
Run this with print(decoder('A' * 1000)) and you'll see why recursion is a bad idea for this task.
Every recursive function must have a base condition that stops the recursion or else the function calls itself infinitely.
To recursively concatenate ONLY the letters of an input string into a single output string:
some_string = "I2L4o2v3e+P;y|t!o#n"
def decoder(encryptedStr, decoded = ""):
if len(encryptedStr) == 0: # Base condition
return decoded
if encryptedStr[0].isalpha():
decoded += encryptedStr[0]
return decoder(encryptedStr[1:], decoded)
# If the char in the index [0] is not a letter it will be sliced out.
return decoder(encryptedStr[1:], decoded)
print(decoder(some_string))
Output:
ILovePython

How to add strings with each other during a loop?

For my programming class, I need to a create a program that takes in a string and two letters as an argument. Whenever the first letter appears in the string, it is replaced with the second letter. I can do this by making the final string into a list. However, our professor has stated that he wants it to be a string, not a list. The code shown below is what I used to make the program work if the final result was to appear in a list.
def str_translate_101(string, x, y):
new_list = []
for i in string:
if i == x:
new_list.append(y)
if i != x:
new_list.append(i)
return new_list
I tried to make one where it would output a string, but it would only return the first letter and the program would stop (which I'm assuming happens because of the "return")
def str_translate_101(string, old, new):
for i in string:
if i == old:
return new
else:
return i
I then tried using the print function, but that didn't help either, as nothing was outputted when I ran the function.
def str_translate_101(string, old, new):
for i in string:
if i == old:
print(new)
else:
print(i)
Any help would be appreciated.
An example of how the result should work when it works is like this:
str_translate_101('abcdcba', 'a', 'x') ---> 'xbcdcbx'
You can use join to merge a list into a string:
def str_translate_101(string, x, y):
new_list = []
for i in string:
if i == x:
new_list.append(y)
else:
new_list.append(i)
return ''.join(new_list)
or use the one-liner
str_tranlsate_101 = str.replace
The simplest solution would be, instead of storing the character in a list you can simply declare an empty string and in the 'if' block append the character to the string using the augmented '+=' operator. E.g.
if i == x:
concat_str += y
As for the return, basically, it will break out of the for loop and return to where the function was called from. This is because it only has 1 objective, which once achieved it will not bother to process any further code and simply go back to where the function was called from.

Difference between string.replace(c,'',1)) and string[1:]

So I am trying to trace a recursive function and I'm having difficulties tracing this:
I'm trying to trace a code on permutations but this confuses me:
# doesn't work
def permutations(string):
if len(string) == 1:
return string
recursive_perms = []
for c in string:
for perm in permutations(string[1:]):
recursive_perms.append(c+perm)
return set(recursive_perms)
# works
def permutations(string):
if len(string) == 1:
return string
recursive_perms = []
for c in string:
for perm in permutations(string.replace(c,'',1)):
recursive_perms.append(c+perm)
return set(recursive_perms)
I'm horrible at tracing recursion currently, and I don't know the difference between the first and second function but the 2nd one works first one doesn't. The difference is the replace. Whats the different between the replace and doing string[1:]? Is there anyway you could change the replace into string slicing?
str.replace(old, new[, count]) replaces old with new, optionally only for the first count instances
str[1:] returns a slice of str from the character at the second index until the last index, effectively returning the entire string except for it's first character.
What really happens when you call str[1:] is that you're calling str.__getitem__(slice(1, None))

Function Calling Python basic

I'm trying to enter values from the tuple ('a',1), ('b',2),('c',3) into the function dostuff but i always get a return of None or False. i'm new to this to i'm sorry if this question is basic. I would appreciate any help.
I expect the result of this to be:
a1---8
b2---8
c3---8
Code:
def dostuff(stri,numb,char):
cal = stri+str(numb)+'---'+str(char)
return cal
def callit (tups,char):
for x in range(len(tups)):
dostuff(tups[x][0],tups[x][1],char)
print(callit([('a',1), ('b',2),('c',3)],8))
I think you're misunderstanding the return value of the functions: unless otherwise specified, all functions will return None at completion. Your code:
print(callit([('a',1), ('b',2),('c',3)],8))`
is telling the Python interpreter "print the return value of this function call." This isn't printing what you expect it to because the callit function doesn't have a return value specified. You could either change the return in your dostuff function like so:
def dostuff(stri,numb,char):
cal = stri+str(numb)+'---'+str(char)
print cal
def callit (tups,char):
for x in range(len(tups)):
dostuff(tups[x][0],tups[x][1],char)
callit([('a',1), ('b',2),('c',3)],8)
This changes the return on the third line into a print command, and removes the print command from the callit call.
Another option would be:
def dostuff(stri,numb,char):
cal = stri+str(numb)+'---'+str(char)
return cal
def callit (tups,char):
for x in range(len(tups)):
cal = dostuff(tups[x][0],tups[x][1],char)
print(cal)
callit([('a',1), ('b',2),('c',3)],8)
This takes the return value from the dostuff function and stores it in a variable named cal, which could then be printed or written to a file on disk.
as #n1c9 said, every Python function must return some object, and if there's no return statement written in the function definition the function will implicitly return the None object. (implicitly meaning that under the hood, Python will see that there's no return statement and returnNone)
However, while there's nothing wrong in this case with printing a value in a function rather than returning it, it's generally considered bad practice. This is because if you ever want to test the function to aid in debugging, you have to write the test within the function definition. While if you returned the value you could just test the return value of calling the function.
So when you're debugging this code you might write something like this:
def test_callit():
tups = [('a', 1), ('b', 2), ('c', 3)]
expected = 'a1---8\nb2---8\nc3---8'
result = callit(tups, 8)
assert result == expected, (str(result) + " != " + expected)
if you're unfamiliar with the assert statement, you can read up about it here
Now that you have a test function, you can go back and modify your code. Callit needs a return value, which in this case should probably be a string. so for the functioncallit you might write
def callit(tups, char):
result = ''
for x in range(len(tups)):
result += dostuff(tups[x][0], tups[x][1], char) + '\n'
result = result[:result.rfind('\n')] # trim off the last \n
return result
when you run test_callit, if you get any assertion errors you can see how it differs from what you expect in the traceback.
What I'm about to talk about isn't really relevant to your question, but I would say improves the readability of your code.
Python's for statement is very different from most other programming languages, because it actually acts like a foreach loop. Currently, the code ignores that feature and forces regular for-loop functionality. it's actually simpler and faster to write something like this:
def callit(tups, char):
result = ''
for tup in tups:
result += dostuff(tup[0], tup[1], char) + '\n'
result = result[:result.rfind('\n')] # trim off the last \n
return result
For a function to return a value in python it must have a return statement. In your callit function you lack a value to return. A more Pythonic approach would have both a value and iterate through the tuples using something like this:
def callit(tups, char):
x = [dostuff(a, b, char) for a, b in tups]
return x
Since tups is a list of tuples, we can iterate through it using for a, b in tups - this grabs both elements in the pairs. Next dostuff(a, b, char) is calling your dostuff function on each pair of elements and the char specified. Enclosing that in brackets makes the result a list, which we then return using the return statement.
Note you don't need to do:
x = ...
return x
You can just use return [dostuff(a, b, char) for a, b in tups] but I used the former for clarity.
You can use a list comprehension to do it in one line:
char = 8
point_list = [('a', 1), ('b', 2),('c', 3)]
print("\n".join(["{}{}---{}".format(s, n, char) for s, n in point_list]))
"{}{}---{}".format(s, n, char) creates a string by replacing {} by each one of the input in format, therefore "{}{}---{}".format("a", 1, 8) will return "a1---8"
for s, n in point_list will create an implicit loop over the point_list list and for each element of the list (each tuple) will store the first element in s and the second in n
["{}{}---{}".format(s, n, char) for s, n in point_list] is therefore a list created by applying the format we want to each of the tuples: it will return ["a1---8","b2---8","c3---8"]
finally "\n".join(["a1---8","b2---8","c3---8"]) creates a single string from a list of strings by concatenating each element of the list to "\n" to the next one: "a1---8\nb2---8\nc3---8" ("\n" is a special character representing the end of a line

Contradictory outputs in simple recursive function

Note: Goal of the function is to remove duplicate(repeated) characters.
Now for the same given recursive function, different output pops out for different argument:
def rd(x):
if x[0]==x[-1]:
return x
elif x[0]==x[1]:
return rd(x[1: ])
else:
return x[0]+rd(x[1: ])
print("Enter a sentence")
r=raw_input()
print("simplified: "+rd(r))
This functions works well for the argument only if the duplicate character is within the starting first six characters of the string, for example:
if r=abcdeeeeeeefghijk or if r=abcdeffffffghijk
but if the duplicate character is after the first six character then the output is same as the input,i.e, output=input. That means with the given below value of "r", the function doesn't work:
if r=abcdefggggggggghijkde (repeating characters are after the first six characters)
The reason you function don't work properly is you first if x[0]==x[-1], there you check the first and last character of the substring of the moment, but that leave pass many possibility like affffffa or asdkkkkkk for instance, let see why:
example 1: 'affffffa'
here is obvious right?
example 2: 'asdkkkkkk'
here we go for case 3 of your function, and then again
'a' +rd('sdkkkkkk')
'a'+'s' +rd('dkkkkkk')
'a'+'s'+'d' +rd('kkkkkk')
and when we are in 'kkkkkk' it stop because the first and last are the same
example 3: 'asdfhhhhf'
here is the same as example 2, in the recursion chain we arrive to fhhhhf and here the first and last are the same so it leave untouched
How to fix it?, simple, as other have show already, check for the length of the string first
def rd(x):
if len(x)<2: #if my string is 1 or less character long leave it untouched
return x
elif x[0]==x[1]:
return rd(x[1: ])
else:
return x[0]+rd(x[1: ])
here is alternative and iterative way of doing the same: you can use the unique_justseen recipe from itertools recipes
from itertools import groupby
from operator import itemgetter
def unique_justseen(iterable, key=None):
"List unique elements, preserving order. Remember only the element just seen."
# unique_justseen('AAAABBBCCDAABBB') --> A B C D A B
# unique_justseen('ABBCcAD', str.lower) --> A B C A D
return map(next, map(itemgetter(1), groupby(iterable, key)))
def clean(text):
return "".join(unique_justseen(text)
test
>>> clean("abcdefggggggggghijk")
'abcdefghijk'
>>> clean("abcdefghijkkkkkkkk")
'abcdefghijk'
>>> clean("abcdeffffffghijk")
'abcdefghijk'
>>>
and if you don't want to import anything, here is another way
def clean(text):
result=""
last=""
for c in text:
if c!=last:
last = c
result += c
return result
The only issue I found with you code was the first if statement. I assumed you used it to make sure that the string was at least 2 long. It can be done using string modifier len() in fact the whole function can but we will leave it recursive for OP sake.
def rd(x):
if len(x) < 2: #Modified to return if len < 2. accomplishes same as original code and more
return x
elif x[0]==x[1]:
return rd(x[1: ])
else:
return x[0]+rd(x[1: ])
r=raw_input("Enter a sentence: ")
print("simplified: "+rd(r))
I would however recommend not making the function recursive and instead mutating the original string as follows
from collections import OrderedDict
def rd(string):
#assuming order does matter we will use OrderedDict, no longer recursive
return "".join(OrderedDict.fromkeys(string)) #creates an empty ordered dict eg. ({a:None}), duplicate keys are removed because it is a dict
#grabs a list of all the keys in dict, keeps order because list is orderable
#joins all items in list with '', becomes string
#returns string
r=raw_input("Enter a sentence: ")
print("simplified: "+rd(r))
Your function is correct but, if you want to check the last letter, the function must be:
def rd(x):
if len(x)==1:
return x
elif x[0]==x[1]:
return rd(x[1: ])
else:
return x[0]+rd(x[1: ])
print("Enter a sentence")
r=raw_input()
print("simplified: "+rd(r))

Categories

Resources