Python 3.7 : Variable switching between int and str depending on usage - python

I am working on a python Caesar cypher (for fun, I know it's not a good way to encrypt messages) and I ran into a problem. When I run the first bit of code, I get an error saying that the first arg in replace() must be a string, not an integer, when it is in face already a string ("TypeError: replace() argument 1 must be str, not int").
However, whenever I try to use it as an indice for a string, it tells me it is not an int ("TypeError: string indices must be integers").
Here is the code, thanks in advance. (There are a few more parts to the code but I don't think they're relevant to the question.)
def find_str(s, char):
index = 0
if char in s:
c = char[0]
for ch in s:
if ch == c:
if s[index:index+len(char)] == char:
return index
index += 1
return -1
class Alpha:
def __init__(self, message, key):
self.fKey = key
self.msg = str(message)
self.alpha = []
self.spcLoc = []
self.spcNum = 0
self.encryptedMessage = str(self.msg)
def encMsg(self):
for letter in self.spcNum):
str.replace(letter, find_str(self.alpha,letter) + self.fKey, self.spcNum)
def main():
msg = 'This is sparta'
key = 1
a = Alpha(msg, key)
a.encMsg()

for letter in self.spcNum:
This is a for-each loop which loops over every value in self.spcNum.
For example
for letter in ['a','b','c']:
print(letter)
will print out the letters a, b and c.
You can not iterate over self.spcNum. Because it is an integer (with the value 0) not a list.
There are other problems in the code too,
str.replace(letter, find_str(self.alpha,letter) + self.fKey, self.spcNum)
You're using this method incorrectly.
Correct usage:
stringYouWantEdited = "hi, my name is DGGB, hi"
substringYouWantReplaced = "hi"
newSubstring = "hello"
numberOfTimesThisShouldHappen = 1
newString = stringYouWantEdited.replace(substringYouWantReplaced , newSubstring , numberOfTimesThisShouldHappen )
print(newString)

Related

Efficient partial search of a trie in python

This is a hackerrank exercise, and although the problem itself is solved, my solution is apparently not efficient enough, so on most test cases I'm getting timeouts. Here's the problem:
We're going to make our own Contacts application! The application must perform two types of operations:
add name, where name is a string denoting a contact name. This must store as a new contact in the application.
find partial, where partial is a string denoting a partial name to search the application for. It must count the number of contacts starting with partial and print the count on a new line.
Given n sequential add and find operations, perform each operation in order.
I'm using Tries to make it work, here's the code:
import re
def add_contact(dictionary, contact):
_end = '_end_'
current_dict = dictionary
for letter in contact:
current_dict = current_dict.setdefault(letter, {})
current_dict[_end] = _end
return(dictionary)
def find_contact(dictionary, contact):
p = re.compile('_end_')
current_dict = dictionary
for letter in contact:
if letter in current_dict:
current_dict = current_dict[letter]
else:
return(0)
count = int(len(p.findall(str(current_dict))) / 2)
re.purge()
return(count)
n = int(input().strip())
contacts = {}
for a0 in range(n):
op, contact = input().strip().split(' ')
if op == "add":
contacts = add_contact(contacts, contact)
if op == "find":
print(find_contact(contacts, contact))
Because the problem requires not returning whether partial is a match or not, but instead counting all of the entries that match it, I couldn't find any other way but cast the nested dictionaries to a string and then count all of the _end_s, which I'm using to denote stored strings. This, it would seem, is the culprit, but I cannot find any better way to do the searching. How do I make this work faster? Thanks in advance.
UPD:
I have added a results counter that actually parses the tree, but the code is still too slow for the online checker. Any thoughts?
def find_contact(dictionary, contact):
current_dict = dictionary
count = 0
for letter in contact:
if letter in current_dict:
current_dict = current_dict[letter]
else:
return(0)
else:
return(words_counter(count, current_dict))
def words_counter(count, node):
live_count = count
live_node = node
for value in live_node.values():
if value == '_end_':
live_count += 1
if type(value) == type(dict()):
live_count = words_counter(live_count, value)
return(live_count)
Ok, so, as it turns out, using nested dicts is not a good idea in general, because hackerrank will shove 100k strings into your program and then everything will slow to a crawl. So the problem wasn't in the parsing, it was in the storing before the parsing. Eventually I found this blogpost, their solution passes the challenge 100%. Here's the code in full:
class Node:
def __init__(self):
self.count = 1
self.children = {}
trie = Node()
def add(node, name):
for letter in name:
sub = node.children.get(letter)
if sub:
sub.count += 1
else:
sub = node.children[letter] = Node()
node = sub
def find(node, data):
for letter in data:
sub = node.children.get(letter)
if not sub:
return 0
node = sub
return node.count
if __name__ == '__main__':
n = int(input().strip())
for _ in range(n):
op, param = input().split()
if op == 'add':
add(trie, param)
else:
print(find(trie, param))

Formatting and Understanding the Process for an "Unjumbler"

Basically, the code is supposed to print out what it believes to be the unjumbled letters based on the amount of one letter in an index. When I run it, it keeps saying stringlist is not defined. Any idea why? Could use some help with formatting too.
def getMessages():
stringlist=[]
stringinput=""
while stringinput!="DONE":
stringinput=input("Type each string. When you are finished, type DONE. ")
if stringinput=="DONE":
return stringlist
else:
stringlist.append(stringinput)
def countFrequencies(stringlist, indexval):
letterdict={"a":0, "b":0, "c":0, "d":0, "e":0, "f":0, "g":0, "h":0, "i":0, "j":0, "k":0, "l":0,
"m":0, "n":0, "o":0, "p":0, "q":0, "r":0, "s":0, "t":0, "u":0, "v":0, "w":0, "x":0,
"y":0, "z":0}
for i in stringlist:
counter=i[indexval]
letterdict[counter]+=1
return letterdict
def mostCommonLetter(letterdict):
ungarble=""
highest=-1
for i in letterdict.keys():
if letterdict[i]>highest:
ungarble=i
highest=letterdict[i]
return ungarble
getMessages()
countFrequencies(stringlist, indexval)
print("Recovered message: ", mostCommonLetter(letterdict))
Your indentation is incorrect.
You could use Counter to aggregate the frequencies of letters in each line.
from collections import Counter
def getMessages():
stringlist=[]
stringinput=""
while stringinput!="DONE":
stringinput=input("Type each string. When you are finished, type DONE. ")
if stringinput=="DONE":
return stringlist
else:
stringlist.append(stringinput)
def countFrequencies(stringlist):
frequencies = Counter()
for line in stringlist:
frequencies.update(line)
return frequencies
def mostCommonLetter(frequencies):
return max(frequencies)
stringlist = getMessages()
frequencies = countFrequencies(stringlist)
print("Recovered message: ", mostCommonLetter(frequencies))

OOP python program

from collections import Counter
class Runlength:
def __init__(self):
self.str = 0
def returner(self,str):
self.str = str
self.__str = ','.join(str(n) for n in self.__str)
self.__str = self.__str[::-1]
self.__str = self.__str.replace(',', '')
return self.__str
def final(self,num):
self.num = num
k = []
c = Counter(self.num).most_common()
for x in c:
k += x
return k
math = Runlength()
def Main():
a = "aabbcc"
b = math.returner(a)
c = math.final(b)
print(c)
Main()
The program takes a word as input and gives the occurrence of each repeating character and
outputs that number along with a single character of the repeating sequence.
I cant figure it out, why this doesn't work. I get this error:
NameError: global name 'returner' is not defined
The problem is that in Main() you are not accessing the global (outside the scope of the Main() method) math variable. Instead try initializing your math inside the Main() function
This lets the method know that it should use the global math variable instead of trying to look for a non-existent local one.
I got this error with your code:
self.__str = ','.join(str(n) for n in self.__str)
AttributeError: Runlength instance has no attribute '_Runlength__str'
Maybe you mean:
self.__str = ','.join(str(n) for n in self.str
And choose input argument for returner() method as str_ not str, cause str -- is the name of python built-in type, so better to not choose variable names with built-in type names.
So after this changes I got this output:
['a', 2, 'c', 2, 'b', 2]
So my python version is 2.7.3 and error you've got does not appear with my python version.
What python version you use to compile your code? If this python3 it works fine too.So try this code, it works fine for me:
from collections import Counter
class Runlength:
def __init__(self):
self.str = 0
def returner(self,str_):
self.string = str_
self.__str = ','.join(str(n) for n in self.string)
self.__str = self.__str[::-1]
self.__str = self.__str.replace(',', '')
return self.__str
def final(self,num):
self.num = num
k = []
c = Counter(self.num).most_common()
for x in c:
k += x
return k
math = Runlength()
def Main():
a = "aabbcc"
b = math.returner(a)
c = math.final(b)
print(c)
Main()
def Main():
math = Runlength()
a = "aabbcc"
b = math.returner(a)
c = math.final(b)
print(c)
Main()
This should work fine..
But I observed that the object can even be accessed if it is not declared as global. Is their any explantion for it in the above scenario?

Implement python replace() function without using regexp

I'm trying to rewrite the equivalent of the python replace() function without using regexp. Using this code, i've managed to get it to work with single chars, but not with more than one character:
def Replacer(self, find_char, replace_char):
s = []
for char in self.base_string:
if char == find_char:
char = replace_char
#print char
s.append(char)
s = ''.join(s)
my_string.Replacer('a','E')
Anybody have any pointers how to make this work with more than one character? example:
my_string.Replacer('kl', 'lll')
How clever are you trying to be?
def Replacer(self, find, replace):
return(replace.join(self.split(find)))
>>> Replacer('adding to dingoes gives diamonds','di','omg')
'adomgng to omgngoes gives omgamonds'
Here is a method that should be pretty efficient:
def replacer(self, old, new):
return ''.join(self._replacer(old, new))
def _replacer(self, old, new):
oldlen = len(old)
i = 0
idx = self.base_string.find(old)
while idx != -1:
yield self.base_string[i:idx]
yield new
i = idx + oldlen
idx = self.base_string.find(old, i)
yield self.base_string[i:]
Let's try with some slices (but you really should consider using the builtin method of python) :
class ReplacableString:
def __init__(self, base_string):
self.base_string =base_string
def replacer(self, to_replace, replacer):
for i in xrange(len(self.base_string)):
if to_replace == self.base_string[i:i+len(to_replace)]:
self.base_string = self.base_string[:i] + replacer + self.base_string[i+len(to_replace):]
def __str__(self):
return str(self.base_string)
test_str = ReplacableString("This is eth string")
test_str.replacer("eth", "the")
print test_str
>>> This is the string

Python string assignment issue!

So I'm fairly new to Python but I have absolutely no idea why this strong oldUser is changing to current user after I make the parse call. Any help would be greatly appreciated.
while a < 20:
f = urllib.urlopen("SITE")
a = a+1
for i, line in enumerate(f):
if i == 187:
print line
myparser.parse(line)
if fCheck == 1:
result = oldUser[0] is oldUser[1]
print oldUser[0]
print oldUser[1]
else:
result = user is oldUser
fCheck = 1
print result
user = myparser.get_descriptions(firstCheck)
firstCheck = 1
print user
if result:
print "SAME"
array[index+1] = array[index+1] +0
else:
oldUser = user
elif i > 200:
break
myparser.reset()
I don't understand why result doesn't work either... I print out both values and when they're the same it's telling me they're not equal... Also, why does myparser.parse(line) turn oldUser into a size 2 array? Thanks!
** Here's the definition for myparse...
class MyParser(sgmllib.SGMLParser):
"A simple parser class."
def parse(self, s):
"Parse the given string 's'."
self.feed(s)
self.close()
def __init__(self, verbose=0):
"Initialise an object, passing 'verbose' to the superclass."
sgmllib.SGMLParser.__init__(self, verbose)
self.divs = []
self.descriptions = []
self.inside_div_element = 0
def start_div(self, attributes):
"Process a hyperlink and its 'attributes'."
for name, value in attributes:
if name == "id":
self.divs.append(value)
self.inside_div_element = 1
def end_div(self):
"Record the end of a hyperlink."
self.inside_div_element = 0
def handle_data(self, data):
"Handle the textual 'data'."
if self.inside_div_element:
self.descriptions.append(data)
def get_div(self):
"Return the list of hyperlinks."
return self.divs
def get_descriptions(self, check):
"Return a list of descriptions."
if check == 1:
self.descriptions.pop(0)
return self.descriptions
Don’t compare strings with is. That checks if they’re the same object, not two copies of the same string. See:
>>> string = raw_input()
hello
>>> string is 'hello'
False
>>> string == 'hello'
True
Also, the definition of myparser would be useful.
I'm not quite sure what your code is doing, but I suspect you want to use == instead of is. Using is compares object identity, which is not the same as string equality. Two different string objects may contain the same sequence of characters.
result = oldUser[0] == oldUser[1]
If you're curious, for more information on the behaviour of the is operator see Python “is” operator behaves unexpectedly with integers.

Categories