Function that handles a file reading - python

I have a file that I have to read. In every line, there is a name, age, height, and weight. There are a lot of lines in the file and I only need the names. Here is my code:
import random
import string
dictionary = {}
lst = []
with open("persons.dat","r") as file:
for line in file:
items = line.split(',') #this makes this ['Bill Johnson', '31', '196', '93']
key = items[0]
dictionary[key] = []
for i in range(1,len(items)):
dictionary[key].append(int(items[i]))
#print(dictionary)
for key in dictionary.keys():
lst.append(key)
print(lst)
def generateGroup(sizeOfGroup):
for names in range(sizeOfGroup):
random_names = random.choice(lst)
print(random_names)
My code gets all of the names in the list as intended. The code works fine up to generateGroup()
I need a function that would ask for the size of a group (some number) of the list and give random names from that list.
I don't know how to implement that function. I kind of get the logic of the function, but I don't know where I should put the function in the code (like which line).

random.sample does exactly this.
def generateGroup(sizeOfGroup):
print(random.sample(lst, k=sizeOfGroup))
random.sample returns a list. You could accumulate the list yourself
random_names = []
for names in range(sizeOfGroup):
random_names.append(random.choice(lst))
print(random_names)
but random.sample ensures that you won't select the same name twice.
Once generateGroup is defined correctly, you still need to call it with an argument:
while True:
try:
n = int(input("Enter a number: "))
break
except ValueError:
print("That's not an integer, try again")
generateGroup(n)

Related

Python how to create sublists in a loop

I have a python question about the following code block.
The expected output is a sublist, containing the name that is entered into the list, turn it into a gmail account and add a username that only shows the first 4 letters of the input. I currently have this code:
def database(L):
result = []
for i in L:
username = i[0:4]
result.append(i + '#gmail.com')
result.append(username)
return result
accounts = database(['Bakerfield', 'Thomas'])
print(accounts)
I currently get the output
['Bakerfield#gmail.com', 'Bake' , 'Thomas#gmail.com', 'Thom']
But I would like the output:
[['Bakerfield#gmail.com', 'Bake'], ['Thomas#gmail.com', 'Thom']]
This is because i am not using sublists, and I am unsure how to divide this function loop into one that creates a sublist out of these accounts and essentially seperates these lists.
Thanks in advance!!
Just do one append, but with a list:
def database(L):
result = []
for i in L:
username = i[0:4]
result.append([i + '#gmail.com', username])
return result
Output as required.
Your only issue is which the result.append section. Take a look at this revised code:
def database(L):
result = []
for i in L:
username = i[0:4]
result.append([i + '#gmail.com'])
result.append([username])
return result
accounts = database(['Bakerfield', 'Thomas'])
print(accounts)
Notice that you just forgot a single pair of brackets when using the append() function, this should fix it permanently.
I would use a dataclass for this. also, only one append required after instantiation.
from dataclasses import dataclass
#dataclass
class UsrEmial:
email: str
naem: str
def database(L):
result = []
for i in L:
username = i[0:4]
e_obj_for_usr = UsrEmial(f'{i}#gmail.com', username)
result.append(e_obj_for_usr)
# not do:
# result.append(username)
# reasoning: `UsrEmial` object contains already this info.
# here not do (2) appends, instead using only one (1) as above:
# result.append(i + '#gmail.com')
# result.append(username)
# reasoning: two appends will add to list but at the same level,
# such that 1st and 2nd append result will be as siblings with
# all the others in a list.
return result
accounts = database(['Bakerfield', 'Thomas'])
print(accounts)
# OK
assert accounts[-1].naem == 'Thomas'[0:4]
Out:
[UsrEmial(email='Bakerfield#gmail.com', naem='Bake'), UsrEmial(email='Thomas#gmail.com', naem='Thom')]

Get an expression as an input in python

I have an array of numbers that the user input. Now, I want the user to enter an expression (for example, sin (x)) and I want to evaluate the expression using the numbers from the array.
I don't know how to get the expression as an input from the user and then to evaluate it based on the array.
So far I have:
collection = list()
number = input("Enter the number of elements you want: ")
for i in range(int(number)):
n = input("Enter number:")
collection.append(int(n))
print ('ARRAY: ',collection)
def function_creator():
expr = input("Enter the function(in terms of x):")
x = int(input("Enter the value of x:"))
safe_dict['x'] = x
y = eval(expr, {"__builtins__":None}, safe_dict)
print("y = {}".format(y))
if __name__ == "__main__":
safe_list = ['acos', 'asin', 'atan', 'atan2', 'ceil', 'cos',
'cosh', 'degrees', 'e', 'exp', 'fabs', 'floor',
'fmod', 'frexp', 'hypot', 'ldexp', 'log', 'log10',
'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt',
'tan', 'tanh']
safe_dict = dict([(k, locals().get(k, None)) for k in safe_list])
function_creator()
I have tried getting the expression using the eval() function in python but couldn't get it to work. For the sake of working in steps, I'm not using the array right now as the x variable in the expression the user inputs. Instead, I'm just trying to evaluate the expression the user inputs with an x of their choice. Any ideas on how to make it work or any other way in which I can get the function as an input?
This code should work for expressions in x such as x*sin(x) + (x+1)*exp(2*x)
from math import * # import sin, cos, tan, etc. into globals
# to allow use by eval
def function_creator(arr):
" allows expressions such as x*sin(x) + x "
expr = input("Enter the function(in terms of x):")
# x is bound locally inside the list comprehension for use by eval
return [eval(expr) for x in arr] # compute expression on each data element
if __name__ == "__main__":
collection = list()
number = input("Enter the number of elements you want: ")
for i in range(int(number)):
n = input("Enter number:")
collection.append(float(n))
print ('ARRAY: ',collection)
result = function_creator(collection)
print(result)
Simple answer
You can import functions into the global scope, and then grab them from globals as follows:
from numpy import sin
collection = list()
number = input("Enter the number of elements you want: ")
for i in range(int(number)):
n = input("Enter number:")
collection.append(int(n))
print ('ARRAY: ',collection)
func_name = input("Enter function: ")
try:
ans = globals()[func_name](collection)
except KeyError:
raise AttributeError('Function not found in namespace!')
print(ans)
Slightly more sophisticated answer
You can try the following code snippet:
import numpy as np
collection = list()
number = input("Enter the number of elements you want: ")
for i in range(int(number)):
n = input("Enter number:")
collection.append(int(n))
print ('ARRAY: ',collection)
func_name = input("Enter function: ")
def getattr_loop(obj,attr):
'''getattr, or getitem, or error, in a loop, forever!'''
spl = attr.split('.')
if len(spl) > 1:
try:
return getattr_loop(getattr(obj,spl[0]),'.'.join(spl[1:]))
except AttributeError as err:
try:
return getattr_loop(obj[spl[0]],'.'.join(spl[1:]))
except KeyError:
raise err
else:
return getattr(obj,spl[0])
# Compute using user defined function
try:
ans = getattr_loop(globals(),func_name)(collection)
except AttributeError:
raise AttributeError('Function not found in namespace!')
print(ans)
getattr_loop is a function which will recursively search obj for attribute attr. The attribute given by attr automatically reads dot notation, and so you can use it to do some nifty tricks. It also has some handling to attempt .__getitem__() if there is no attribute (obj[attr])
The downside is that getattr_loop will give you a RecursionError if you have a really deeply nested attribute in obj.
For example, if one uses the Numpy module as the object:
>>> getattr_loop(np,'random.rand')
This would allow you to access np.random.rand. If you use globals() as is used in the code snippet, it will grab all objects defined at the global scope. So, a user of the above code snippet can type 'np.sin' as the function, and the code will compute np.sin(collection).
If you wish to use sin directly, you'll need to import that directly into the namespace.
Caveat about security
Grabbing things from globals() can be risky from a security perspective. If you need this to be secure, make sure to:
sanitise your inputs
construct an object which only contains the functions that you want someone to be able to access, lest you accidentally allow someone to patch into something like os.remove and do some harm...
Enjoy!

Printing input from user

It doesn't want to print out the list entered by the user. I think the problem is in list= []
from HeapClass import Heap
def minHeap(list):
heap = Heap() #
lst = eval(input("Enter list of numbers: "))
for v in lst:
heap.add(v)
for i in range(len(list)):
list[len(list) - 1 - i] = heap.remove()
def main():
list = [] This think the problem is here because it doesn't return a list but when I write list = lst... It does not work either
minHeap(list)
for v in list:
print(str(v)+ " ", end = " ")
main()
You're using list as a variable name. Rename it to something else here def minHeap(list):, here def main(): list = [] and everywhere else. You don't need it.
Also, you don't need the evil eval() :)
If you want user input of the format "1,2,3" (comma-separated numbers) to be cast to a list of integers, you could instead do:
lst = list(input("Enter list of numbers: ")) in python2.7
or
lst = [int(x) for x in input("Enter list of numbers: ").split(",")] in python3.
To see why using list as a variable name is bad/confusing, try the following:
Type list((1,2,3)) in your interpreter. It should transform the tuple (1,2,3) to a list and return [1, 2, 3]. All is well.
Now try typing list = [] and repeat list((1,2,3)).
You should now get an error saying TypeError: 'list' object is not callable and you won't be able to cast anything to list again; it effectively overrides the datatype so what used to be <type 'type'> is now a single list of <type 'list'>. Overriding built-in stuff gets weird; best to avoid it unless you're intentionally doing meta-programming.

Issue with dict() in Python, TypeError:'tuple' object is not callable

I'm trying to make a function that will take an arbritrary number of dictionary inputs and create a new dictionary with all inputs included. If two keys are the same, the value should be a list with both values in it. I've succeded in doing this-- however, I'm having problems with the dict() function. If I manually perform the dict function in the python shell, I'm able to make a new dictionary without any problems; however, when this is embedded in my function, I get a TypeError. Here is my code below:
#Module 6 Written Homework
#Problem 4
dict1= {'Fred':'555-1231','Andy':'555-1195','Sue':'555-2193'}
dict2= {'Fred':'555-1234','John':'555-3195','Karen':'555-2793'}
def dictcomb(*dict):
mykeys = []
myvalues = []
tupl = ()
tuplist = []
newtlist = []
count = 0
for i in dict:
mykeys.append(list(i.keys()))
myvalues.append(list(i.values()))
dictlen = len(i)
count = count + 1
for y in range(count):
for z in range(dictlen):
tuplist.append((mykeys[y][z],myvalues[y][z]))
tuplist.sort()
for a in range(len(tuplist)):
try:
if tuplist[a][0]==tuplist[a+1][0]:
comblist = [tuplist[a][1],tuplist[a+1][1]]
newtlist.append(tuple([tuplist[a][0],comblist]))
del(tuplist[a+1])
else:
newtlist.append(tuplist[a])
except IndexError as msg:
pass
print(newtlist)
dict(newtlist)
The error I get is as follows:
Traceback (most recent call last):
File "<pyshell#17>", line 1, in <module>
dictcomb(dict1,dict2)
File "C:\Python33\M6HW4.py", line 34, in dictcomb
dict(newtlist)
TypeError: 'tuple' object is not callable
As I described above, in the python shell, print(newtlist) gives:
[('Andy', '555-1195'), ('Fred', ['555-1231', '555-1234']), ('John', '555-3195'), ('Karen', '555-2793')]
If I copy and paste this output into the dict() function:
dict([('Andy', '555-1195'), ('Fred', ['555-1231', '555-1234']), ('John', '555-3195'), ('Karen', '555-2793')])
The output becomes what I want, which is:
{'Karen': '555-2793', 'Andy': '555-1195', 'Fred': ['555-1231', '555-1234'], 'John': '555-3195'}
No matter what I try, I can't reproduce this within my function. Please help me out! Thank you!
A typical example of why keywords should not be used as variable names. Here dict(newtlist) is trying to call the dict() builtin python, but there is a conflicting local variable dict. Rename that variable to fix the issue.
Something like this:
def dictcomb(*dct): #changed the local variable dict to dct and its references henceforth
mykeys = []
myvalues = []
tupl = ()
tuplist = []
newtlist = []
count = 0
for i in dct:
mykeys.append(list(i.keys()))
myvalues.append(list(i.values()))
dictlen = len(i)
count = count + 1
for y in range(count):
for z in range(dictlen):
tuplist.append((mykeys[y][z],myvalues[y][z]))
tuplist.sort()
for a in range(len(tuplist)):
try:
if tuplist[a][0]==tuplist[a+1][0]:
comblist = [tuplist[a][1],tuplist[a+1][1]]
newtlist.append(tuple([tuplist[a][0],comblist]))
del(tuplist[a+1])
else:
newtlist.append(tuplist[a])
except IndexError as msg:
pass
print(newtlist)
dict(newtlist)
You function has a local variable called dict that comes from the function arguments and masks the built-in dict() function:
def dictcomb(*dict):
^
change to something else, (*args is the typical name)
Do you need to implement this entirely yourself, or would it be okay to use defaultdict? If so, you might do something like:
from collections import defaultdict
merged_collection = defaultdict(list)
collection_1= {'Fred':'555-1231','Andy':'555-1195','Sue':'555-2193'}
collection_2= {'Fred':'555-1234','John':'555-3195','Karen':'555-2793'}
for collection in (collection_1, collection_2):
for name, number in collection.items():
merged_collection[name].append(number)
for name, number in merged_collection.items():
print(name, number)
John ['555-3195']
Andy ['555-1195']
Fred ['555-1231', '555-1234']
Sue ['555-2193']
Karen ['555-2793']

Why can't I search through the dictionary I created (Python)?

In this program I am making a dictionary from a plain text file, basically I count the amount a word occurs in a document, the word becomes the key and the amount of time it occurs is the value. I can create the dictionary but then I cannot search through the dictionary. Here is my updated code with your guys' input. I really appreciate the help.
from collections import defaultdict
import operator
def readFile(fileHandle):
d = defaultdict(int)
with open(fileHandle, "r") as myfile:
for currline in myfile:
for word in currline.split():
d[word] +=1
return d
def reverseLookup(dictionary, value):
for key in dictionary.keys():
if dictionary[key] == value:
return key
return None
afile = raw_input ("What is the absolute file path: ")
print readFile (afile)
choice = raw_input ("Would you like to (1) Query Word Count (2) Print top words to a new document (3) Exit: ")
if (choice == "1"):
query = raw_input ("What word would like to look up? ")
print reverseLookup(readFile(afile), query)
if (choice == "2"):
f = open("new.txt", "a")
d = dict(int)
for w in text.split():
d[w] += 1
f.write(d)
file.close (f)
if (choice == "3"):
print "The EXIT has HAPPENED"
else:
print "Error"
Your approach is very complicated (and syntactically wrong, at least in your posted code sample).
Also, you're rebinding the built-in name dict which is problematic, too.
Furthermore, this functionality is already built-in in Python:
from collections import defaultdict
def readFile(fileHandle):
d = defaultdict(int) # Access to undefined keys creates a entry with value 0
with open(fileHandle, "r") as myfile: # File will automatically be closed
for currline in myfile: # Loop through file line-by-line
for word in currline.strip().split(): # Loop through words w/o CRLF
d[word] +=1 # Increase word counter
return d
As for your reverseLookup function, see ypercube's answer.
Your code returns after it looks in the first (key,value) pair. You have to search the whole dictionary before returning that the value has not been found.
def reverseLookup(dictionary, value):
for key in dictionary.keys():
if dictionary[key] == value:
return key
return None
You should also not return "error" as it can be a word and thus a key in your dictionary!
Depending upon how you're intending to use this reverseLookup() function, you might find your code much happier if you employ two dictionaries: build the first dictionary as you already do, and then build a second dictionary that contains mappings between the number of occurrences and the words that occurred that many times. Then your reverseLookup() wouldn't need to perform the for k in d.keys() loop on every single lookup. That loop would only happen once, and every single lookup after that would run significantly faster.
I've cobbled together (but not tested) some code that shows what I'm talking about. I stole Tim's readFile() routine, because I like the look of it more :) but took his nice function-local dictionary d and moved it to global, just to keep the functions short and sweet. In a 'real project', I'd probably wrap the whole thing in a class to allow arbitrary number of dictionaries at run time and provide reasonable encapsulation. This is just demo code. :)
import operator
from collections import defaultdict
d = defaultdict(int)
numbers_dict = {}
def readFile(fileHandle):
with open(fileHandle, "r") as myfile:
for currline in myfile:
for word in currline.split():
d[word] +=1
return d
def prepareReverse():
for (k,v) in d.items():
old_list = numbers_dict.get(v, [])
new_list = old_list << k
numbers_dict[v]=new_list
def reverseLookup(v):
numbers_dict[v]
If you intend on making two or more lookups, this code will trade memory for execution speed. You only iterate through the dictionary once (iteration over all elements is not a dict's strong point), but at the cost of duplicate data in memory.
The search is not working because you have a dictionary mapping a word to its count, so getting the number of occurrences for a 'word' should be just dictionary[word]. You don't really need the reveseLookup(), there is already a .get(key, default_value) method in dict: dictionary.get(value, None)

Categories