Python, named tuple, expanding on making a basic part invntory system - python

I have been using this basic code for a simple inventory system. I need to expand this to keep track of another list of parts that are leaving the inventory. I thought I might be able to do this by creating another namedtuple for the data structure . I have some problems because the two namedtuples would need to exist and be accessed at the same time. How can I do this in python?
import sys
from collections import namedtuple
class Part(namedtuple("Part", "name part_number quantity")):
def __str__(self):
return ", ".join(self)
class Parts(dict):
def display (self):
if not self:
print('No Parts Found in List')
return
print()
print('Name, Part Number, Quantity')
for part in self.values():
print(part)
print()
def add(self, *args):
try:
name, part_number, quantity = args
except ValueError:
name = input("Enter Name of Part:\n ")
part_number = input("Enter Part Number:\n ")
quantity = input("Enter Quantity:\n ")
self[name] = Part(name, part_number, quantity)
def remove(self, part=""):
if not part:
part = input("Enter Part Name to Remove\n")
try:
del self[part]
except Keyerror:
print("Part {} not found.".format(part))
def edit(self, part=""):
if not part:
part = input("Enter Part Name to Edit\n")
try:
new_name = input("Enter new part name\n")
number = input("Enter new part number\n ")
quantity = input("Enter new quantity\n ")
self[part] = Part(new_name, number, quantity)
except KeyError:
print("No such Part exists: {}".format(part))
def save(self, filename=""):
if not filename:
filename = input("Filename to save: ")
with open(filename, "wt") as out_file:
for part in self.values():
out_file.write("{}\n".format(part))
print("File saved")
def load(self, filename=""):
if not filename:
filename = input("Filename to load: ")
try:
with open(filename, "rt") as in_file:
for line in in_file:
if not line:
break
part, part_number, quantity = line.strip().split(",")
self.add(part, part_number, quantity)
except FileNotFoundError:
print("File Not Found.")
def menu(inventory):
menu_list = [("Parts", inventory.display),
("Add Part", inventory.add),
("Remove Part", inventory.remove),
("Edit Part", inventory.edit),
("Save Part List", inventory.save),
("Load Part List", inventory.load),
("Exit", sys.exit)]
while True:
for i, (name, _) in enumerate(menu_list, 1):
print("{}. {}".format(i, name))
try:
user = int(input("Selection> "))
menu_list[user-1][1]()
except (ValueError, IndexError):
print("Selection Not Valid.")
def main():
print("Welcome to Sticks&Stones Inventory System")
inventory = Parts()
while True:
menu(inventory)
if __name__ == '__main__':
try:
main()
except (KeyboardInterrupt, SystemExit):
print("Exiting Program...")`enter code here
`

Your two tuples (or more) can exist and be accessed at the same time, with no problem!
class Parts(namedtuple("Parts", "name part_number quantity")):
def __str__(self):
return ", ".join(self)
class RemovedParts(namedtuple("RemovedParts", "name quantity")):
pass
# etc.
With that said, let me make some suggestions:
As #juanpa.arrivillaga has already suggested, your current Parts class should not extend dict. Ask yourself, "does my system have a dictionary because it's convenient to the implementation, or is my system a dictionary because of some fundamental property?"
I think you'll see that the has-a/is-a question in this case is a pretty good way to see that you should use composition (has-a) rather than inheritance (is-a) in your design.
Consider renaming your Parts class to something different. What you're modeling is really a thing that contains information about the parts, and how many you have on hand. Perhaps PartsInfo or even Inventory would be a better class name.
Consider separating the actual management object from the user interface. Perhaps you have an Inventory object and an InventoryUI object? Move pretty much all of the functions that interact with the user into the UI class.
Write some helper functions (methods) to do things like read in part numbers or part names and confirm that they are valid. Maybe you need a pair of dictionaries, one for part-number -> part-info and the other for part-name -> part-info?
Consider using the json library to serialize your data for load/save operations.

Related

saving a input value in a dictionary in Python

So I have a dictionary, that translates english to swedish, and would like to let the user add new words to it. However, despite my attempts at updating the dictionary with the input, the values of the dictionary always reset to the base case when re-running Python. Check out the code:
dictionary = {"merry": "god", #base dictionary
"christmas": "jul",
"and": "och",
"happy": "gott",
"new": "nytt",
"year": "ar"
}
def add(): #Prompting user to add words to the dictionary, by asking how many he wants to add ( 0 is an option)
while True:
try:
a = int(input("How many words do you want to add to the dictionary? "))
if a >= 0:
break
else: #don't want negative values
raise ValueError
except ValueError:
print("Not valid ")
return a
for i in range (add()):
key_i = input(f"Enter english word {i + 1}: ")
value_i = input("Enter translation: ")
dictionary[key_i] = value_i
print(dictionary)
Try using this class:
import pickle
class Logger:
def save(self, obj, path):
with open(path, 'wb') as logfile:
pickle.dump(obj, logfile)
def load(self, path):
with open(path, 'rb') as logfile:
new_instance = pickle.load(logfile)
return new_instance
Then, at the beginning of your code create a new instance of the Logger and load (if already existing) the saved dictionary:
path = "./path/to/your/local/directory/" #change this with the path where you have previously saved your dictionary logs
logger = Logger()
dictionary = logger.load(path)
Then, at the end of your code, create a new instance of the logs and save them:
logger.save(dictionary, path)

Making dictionary as user input, then save it into json file

I am trying to do a dictionary database, like actual dictionary. User input key word and meaning and program saves it in database. Like input word: rain , input meaning of the word: water droplets falling from the clouds then program makes it a dictionary. So far I can manage do this but it doesn't work the way I want.
class Mydictionary:
def __init__(self):
self.key=input("Please input word: ")
self.value=input("Please input meaning of the word: ")
def mydictionary(self):
self.dic={self.key:self.value}
Mydic=Mydictionary()
Mydic.mydictionary()
It works for only one time. I want to save keywords and values as much as I want. I want to create a dictionary database.
As far as I could see, it is working perfectly as you explained...
If you were thinking that you want to insert many values in a single object, this won't work as you are getting the only one input while calling the constructor.
You have to implement it like,
import json
class Mydictionary:
def __inint__(self):
self.dic = {}
def mydictionary(self):
self.key=input("Please input word: ")
self.value=input("Please input meaning of the word: ")
self.dic[self.key] = self.value
def save(self, json_file):
with open(json_file, "w") as f:
json.dump(self.dic, f)
Mydic=Mydictionary()
Mydic.mydictionary()
Mydic.mydictionary()
# to save it in a JSON file
Mydic.save("mydict.json")
Now you can call the method n times to add n entries...
You can look at the answer by #arsho below which I would consider as a good practice. Naming the function appropriately wrt the actual function they are doing is important.
To insert new key - value pair to your dictionary, you need to create a method to get data from the user.
In __init__ you can declare an empty dictionary and then in insert method you can get a new entry from the user.
Moreover, to display the current elements of the dictionary you can create a separate method with name display.
json built-in can directly write and read dictionary type data from an to a json file. You can read about json from official documentation on json.
import json
import os
class Mydictionary:
def __init__(self, file_name):
self.json_file = file_name
if os.path.exists(file_name):
with open(self.json_file, "r") as json_output:
self.data = json.load(json_output)
else:
self.data = {}
def insert(self):
user_key = input("Please input word: ")
user_value = input("Please input meaning of the word: ")
self.data[user_key] = user_value
with open(self.json_file, "w") as json_output:
json.dump(self.data, json_output)
def display(self):
if os.path.exists(self.json_file):
with open(self.json_file, "r") as json_output:
print(json.load(json_output))
else:
print("{} is not created yet".format(self.json_file))
Mydic=Mydictionary("data.json")
Mydic.display()
Mydic.insert()
Mydic.insert()
Mydic.display()
Output:
data.json is not created yet
Please input word: rain
Please input meaning of the word: water droplets falling from the clouds
Please input word: fire
Please input meaning of the word: Fire is a chemical reaction that releases light and heat
{'rain': 'water droplets falling from the clouds', 'fire': 'Fire is a chemical reaction that releases light and heat'}
Disclaimer: This is just a concept of class and method declaration and usage. You can improvise this approach.
Try:
import json
class MyDictionary:
__slots__ = "dic",
def __init__(self):
self.dic = {}
def addvalue(self):
"""Adds a value into the dictionary."""
key=input("Please input word: ")
value=input("Please input meaning of the word: ")
self.dic[key] = value
def save(self, json_file):
"""Saves the dictionary into a json file."""
with open(json_file, "w") as f:
json.dump(self.dic, f)
# Testing
MyDic = MyDictionary()
MyDic.addvalue()
MyDic.addvalue()
print(MyDic.dic) # Two elements
MyDic.save("json_file.json") # Save the file
class dictionary():
def __init__(self):
self.dictionary={}
def insert_word(self,word):
self.dictionary.update(word)
def get_word(self):
word=input("enter a word or enter nothing to exit: ")
if word=="":
return None
meaning=input("enter the meaning: ")
return {word:meaning}
def get_dict(self):
return self.dictionary
if __name__ == "__main__":
mydict=dictionary()
word=mydict.get_word()
while word:
mydict.insert_word(word)
word=mydict.get_word()
print(mydict.get_dict())
this will keep taking inputs until you give it a null value and then print out the dictionary when u stop.

Python creating an Object while reading file

I wrote a simple Python script to determine if all students grades are reported. The script first loops through and adds students to arrays regarding grade status. Then I loop through the file again, to determine if each students grades are in. I end up with three arrays that include students with "all grades reported", "some grades reported", "no grades reported". However, I want tackle this problem with more of a object oriented approach. I have attempted to create a class that works. I am stuck at how to loop through and create one Object for each student, then use addcourse to push each course into the Object. Any help I can get to become a better programmer would be great!
Data:
**id,fname,lname,course,grade,mode**
10001,Freddy,Freshman,Art-101,A,online
10001,Freddy,Freshman,Art-101,A,online
10002,Suize,Sophmore,Mat-102,C,inperson
10002,Suize,Sophmore,Bio-101, ,inperson
10002,Suize,Sophmore,Soc-201,D,online
10003,Jilly,Junior,mth-102, ,inperson
10003,Jilly,Junior,Bus-101, ,inperson
10003,Jilly,Junior,Che-204, ,inperson
Working Code:
fh = open('students.txt').readlines()
header = fh.pop(0)
gradereported = []
nogradereported = []
for line in fh:
students = line.split(',')
ids = students[0]
grade = students[4]
if grade != "":
gradereported.append(ids)
else:
nogradereported.append(ids)
allgradesin =[]
nogradesin = []
somegradesin = []
for i in fh:
students = line.split(',')
ids = students[0]
if ids in gradereported and ids not in nogradereported:
if ids not in allgradesin:
allgradesin.append(ids)
elif ids not in gradereported and ids in nogradereported:
if ids not in nogradesin:
nogradesin.append(ids)
elif ids in gradereportedand and ids in nogradereported:
if ids not in somegradesin:
somegradesin.append(ids)
Attempt at class:
class Student(object):
def __init__(self, lname, fname, term, courses = []):
self.studid = studid
self.lname = lname
self.fname = fname
self.term = term
self.courses = []
def addcourse(self, course, grade, mode):
self.course = course
self.grade = grade
self.mode = mode
self.courses.append((self.course, self.grade, self.mode))
You could do this, as #blade suggests, by creating a dictionary indexed by student id and then for each row of your input file either get the existing student from the dictionary if it exists or create a new one. In code, this would look like:
class Student(object):
def __init__(self, student_id, lname, fname):
self.studid = student_id
self.lname = lname
self.fname = fname
self.courses = []
def addcourse(self, course, grade, mode):
self.courses.append((course, grade, mode))
students = {}
fh = open('students.txt').readlines()
header = fh.pop(0)
for line in fh:
row = line.split(',')
if len(row) < 6:
continue
student_id, fname, lname, course, grade, mode = [i.strip() for i in row]
student = students.get(student_id, Student(student_id, lname, fname))
student.addcourse(course, grade, mode)
students[student_id] = student
A couple of things to note. First, I modified the constructor of your Student class, dropping the term argument since it wasn't clear where the term was specified in your input file. Furthermore, since you don't use the courses argument I dropped that as well. (Note that you probably don't want to use [] as a default argument. Read about mutable default arguments here.) You also don't need to create instance variables for the course, grade, and mode in your addcourse function, you can just append them directly to the array.
I also added a call to strip for each of the items pulled from the input file to clean up the newlines at the end of each row.
How about this:
Add a dict that id is the key, and the Student object is the value
Loop the file and if the key is in the dict, get the Student object from the dict. Otherwise create a new Student object. Then add the course to the Student object.
In addition to the answer of #JCVanHanne you could define another function in your class to collect the info, whether a student has none, some or all of his/her grades.
One possible way (assuming a missing grade is represented by an empty string while also grades like A+ or other none-empty values are possible) could be:
def gradeStatus(self):
miss = [course[1] for course in self.courses].count("") # count empty grades
if len(self.courses) == miss:
print('No grades at all')
elif miss in range(1, len(self.courses)):
print('Some, but not all grades')
elif miss == 0:
print('All grades provided')
else:
print('Invalid Data')
You probably would use status codes or other ways (like a return value to further process) to work with the information than just printing them. As an example with the print commands:
students['10003'].gradeStatus() # leads to: No grades at all

Searching text file for a words not working and not throwing any errors

Im trying to search a text file i have for an employees name and it doesn't seem to working but it also isn't throwing any errors. Whenever i search for a word in this file it gets inside the get_the_info function but never reaches the for loop it seems. Im assuming this because ive used print statements to try and figure out where the problem is. Im new to programming but i assume that is common convention to figure out some issues? Anyway heres the code:
import os
import sys
class find_employee:
def __init__(self):
self.get_the_info()
def get_the_info(self):
print "inside get info funct"
self.naples_empschedule = open("schedule.txt","r+")
self.read_schedule = self.naples_empschedule.readlines()
self.name = raw_input(" Enter your first and last name please ")
for line in self.naples_empschedule:
print " now inside for loop"
self.values = aline.split()
if self.name in line:
print ("Name:", self.values[0,1],"\n", "Position:", self.values[3],"\n", "Total Hours:", self.values[11])
else:
print ("You dont work here")
find_employee()
You're mixing classes and functions. Try this instead:
class EmployeeFinder(object):
def __init__(self, path_to_schedule, name=None):
self.name = name or raw_input("Enter your first and last name please: ")
self.path_to_schedule = path_to_schedule
def get_the_info(self):
with open(path_to_schedule, "r") as schedule_file:
for line in schedule_file:
values = line.split()
if self.name in line:
print("Name: " + values[0:1] + "\n" + \
"Position: " + self.values[3] + "\n" \
"Total Hours: ", self.values[11])
# note that this still won't work because values[0:1]
# will return a list, not a string. You might need
# ' '.join(values[0:1]).
else:
print("You don't work here")
employeefinder = EmployeeFinder("path/to/schedule/file", "Adam Smith")
employeefinder.get_the_info()
However it looks like you'd probably be better off with a function, rather than trying to force objects on this. Functional programming is NOT a bad thing.
def find_employee(path_to_schedule, name):
with open(path_to_schedule, "r") as schedule_file:
for line in schedule_file:
if name in line:
values = line.split()
new_name = ' '.join(values[0:1]) # I'm guessing at your intent
position = values[3]
hours = values[11]
print("Name: {}\nPosition: {}\nTotal Hours: {}".format(
new_name, position, hours))
(my last example uses string formatting which is a much better solution than string concatenation)
self.values[0,1]
Here you are trying to index a list with a tuple (0,1) which throws an error. Instead use
self.values[0]
or
self.values[1]
depending on which item you want from the list.
I think your for line in self.naples_empschedule:
should be for line in self.read_schedule:.

How do I catch NameError in Python?

I've been trying to write a simple program in python to use classes and test attributes. It asks the user to input one of the names, and uses that input to display 2 attributes of the name. I've tried to include a try...except block to catch the NameError that occurs when typing something that hasn't been defined as the name of an object, but I still get this traceback:
Traceback (most recent call last):
File "C:/Users/Bede/Documents/Technology/Programming/Python/276/testcode", line 18, in <module>
animalName = input(">")
File "<string>", line 1, in <module>
NameError: name 'Marmadukke' is not defined
I'm aware this probably isn't the best way to do things, so I'm open to all suggestions.
My full code is here:
class Dog(object):
def __init__(self,name,chasesCats):
self.name = name
self.chasesCats = chasesCats
class Cat(object):
def __init__(self,name,chasesMice):
self.name = name
self.chasesMice = chasesMice
Marmaduke = Cat("Marmaduke",True)
Timmy = Cat("Timmy",False)
Cerberus = Dog("Cerberus",True)
Max = Dog("Max",False)
print("Enter Marmaduke, Timmy, Max or Cerberus.")
animalName = input(">")
while 1:
try:
if isinstance(animalName,Cat):
print("Cat")
if animalName.chasesMice:
print("Chases mice")
else: print("Doesn't chase mice")
if isinstance(animalName,Dog):
print("Dog")
if animalName.chasesCats:
print("Chases cats")
else: print("Doesn't chase cats")
break
except NameError:
print("Try again!")
I'm guessing you're using python2.x. In that case, you should use raw_input instead of input. The problem is that on python2.x, input calls eval on the data you put in. I suppose, this means that you could put in the data as "Marmaduke" (note the quotes). But having the program behave differently depending on whether you're using python2.x or 3.x seems undesirable.
An easy way to make the code work for both, python2.x and python3.x:
try:
raw_input
except NameError:
raw_input = input
animalName = input(">") is outside of the try block. So the Error won't be caught.
Probably you want that inside the try block in the loop:
while 1:
try:
animalName = input(">")
if isinstance(animalName,Cat):
This line is the culprit:
animalName = input(">")
When you enter Marmadukke, which isn't defined yet, you get the name error, since you're trying to do:
animalName = Marmadukke #Not defined.
Wrap it in a try/except block:
try:
animalName = input(">")
except:
print("Invalid input!")
But to achieve this, it would be much better to store your animals to a dictionary, and retrieve only the name:
animals = {}
animals['Marmaduke'] = Cat("Marmaduke",True)
animals['Timmy'] = Cat("Timmy",False)
animals['Cerberus'] = Dog("Cerberus",True)
animals['Max'] = Dog("Max",False)
And to retrieve:
animalName = animals[raw_input(">")]
You can then put it inside your while function, and catch KeyError instead of NameError.
Hope this helps!
For the fun and interest, I have extended your code; try tracing through it, you should learn lots ;-)
class Mammal(object):
index = {}
def __init__(self, name, chases_what=type(None)):
Mammal.index[name] = self
self.name = name
self.chases_what = chases_what
def speak(self):
pass
def chase(self, who):
if isinstance(who, self.chases_what):
self.speak()
print('{} chases {} the {}'.format(self.name, who.name, who.__class__.__name__))
who.speak()
else:
print("{} won't chase a {}".format(self.name, who.__class__.__name__))
class Mouse(Mammal):
def speak(self):
print('Squeak! Squeak!')
class Cat(Mammal):
def __init__(self, name, chases=True):
super(Cat, self).__init__(name, Mouse)
self.chases = chases
def chase(self, who):
if self.chases:
super(Cat, self).chase(who)
else:
print("{} won't chase anything".format(self.name))
class Dog(Mammal):
def __init__(self, name, chases_what=Cat):
super(Dog, self).__init__(name, chases_what)
def speak(self):
print('Bark! Bark!')
def chase(self, who):
if self is who:
print("{} chases his own tail".format(self.name))
else:
super(Dog, self).chase(who)
# create animal instances
Mouse('Jerry')
Mouse('Speedy Gonzalez')
Cat('Garfield', chases=False)
Cat('Tom')
Dog('Max')
Dog('Marmaduke', (Cat, Mouse))
def main():
while True:
name = raw_input('Enter an animal name (or Enter to quit): ').strip()
if not name:
break
me = Mammal.index.get(name, None)
if me is None:
print("I don't know {}; try again!".format(name))
continue
chase = raw_input('Enter who they should chase: ').strip()
target = Mammal.index.get(chase, None)
if target is None:
print("I don't know {}".format(name))
else:
me.chase(target)
if __name__=="__main__":
main()

Categories