Day 30 Errors, Exceptions, and Json data
Day 30
Dealing with errors
try : something that might cause an exception
except : do this if there was an exception
else : do this if there were no exception
finally : do this no matter what happens
Example)
# File not found
try:
file = open("a_file.txt")
except:
file = open("a_file.txt", "w")
Python will try to open file, but if it fails, it will write a file instead.
하지만 이런 식으로 쓸 경우 except에 노란줄이 뜨는데 이유는
too broad except clause
try:
file = open("a_file.txt")
a_dict = {"key": "value"}
print(a_dict["asdf"])
except:
file = open("a_file.txt", "w")
이것은 만약 try 에 file not found error가 아닌 다른 종류의 에러가 있을때 그것은 적용이 안 되기 때문이다.
따라서 조금 더 specific하게 적을 필요가 있다.
To fix this problem, you can write it like this instead:
# File not found
try:
file = open("a_file.txt")
a_dict = {"key": "value"}
print(a_dict["asdf"])
except FileNotFoundError:
file = open("a_file.txt", "w")
file.write("something")
FileNotFoundError and KeyError
try:
file = open("a_file.txt")
a_dict = {"key": "value"}
print(a_dict["asdf"])
except FileNotFoundError:
file = open("a_file.txt", "w")
file.write("something")
except KeyError:
print("That key does not exist.")
이럴 경우에는
That key does not exist 만 프린트 되고 에러 메세지는 뜨지 않는다.
만약 에러메세지가 뜨길 원한다면
try:
file = open("a_file.txt")
a_dict = {"key": "value"}
print(a_dict["asdf"])
except FileNotFoundError:
file = open("a_file.txt", "w")
file.write("something")
except KeyError as error_message:
print(f"That key {error_message} does not exist.")
아까와 다르게
That key 'asdf' does not exist. 라고 프린트 된다.
Else: try에서 except로 가지 않는 경우만 실행 (에러 없을 경우)
try:
file = open("a_file.txt")
a_dict = {"key": "value"}
print(a_dict["key"])
except FileNotFoundError:
file = open("a_file.txt", "w")
file.write("something")
except KeyError as error_message:
print(f"That key {error_message} does not exist.")
else:
content = file.read()
print(content)
만약 위에서 에러가 있을 경우에는 print(content)가 실행되지 않는다.
위에서 에러가 없을 경우에만 else statement 가 실행된다.
Finally: 모든 경우에 실행
try:
file = open("a_file.txt")
a_dict = {"key": "value"}
print(a_dict["key"])
except FileNotFoundError:
file = open("a_file.txt", "w")
file.write("something")
except KeyError as error_message:
print(f"That key {error_message} does not exist.")
else:
content = file.read()
print(content)
finally:
file.close()
print("File was closed")
위에서 무슨 에러가 있든 finally 부분은 실행이 된다.
try:
file = open("a_file.txt")
a_dict = {"key": "value"}
print(a_dict["key"])
except FileNotFoundError:
file = open("a_file.txt", "w")
file.write("something")
except KeyError as error_message:
print(f"That key {error_message} does not exist.")
else:
content = file.read()
print(content)
finally:
raise TypeError("This is an error I made up")
또한, raise clause를 사용해 이렇게 위의 진행과 상관 없이 Error를 일으킬 수 있다.
No matter what happens above, you can raise an error.
Excercise
height = float(input("Height: "))
weight = int(input("Weight: "))
bmi = weight / height ** 2
print(bmi)
bmi 프로젝트를 가져와봤다.
If someone input their height to 3m, we know it's obviously an typo.
But this program won't recognise it.
As long as they input any float and integer, it will calculate and print the result.
So, what can I do about it?
height = float(input("Height: "))
weight = int(input("Weight: "))
if height > 3:
raise ValueError
bmi = weight / height ** 2
print(bmi)
Here's one way.
If someone input number that are greater than 3 (which means 3m), it will raise an ValueError.
Coding Room Excercise
- Index Error Handling
We've got some buggy code. Try running the code. The code will crash and give you an IndexError. This is because we're looking through the list of fruits for an index that is out of range.
fruits = ["Apple", "Pear", "Orange"]
#TODO: Catch the exception and make sure the code runs without crashing.
def make_pie(index):
fruit = fruits[index]
print(fruit + " pie")
make_pie(4)
Use what you've learnt about exception handling to prevent the program from crashing. If the user enters something that is out of range just print a default output of "Fruit pie". e.g.
fruits = ["Apple", "Pear", "Orange"]
#TODO: Catch the exception and make sure the code runs without crashing.
def make_pie(index):
try:
fruit = fruits[index]
print(fruit + " pie")
except IndexError:
print("Fruit pie")
make_pie(4)
here's what I did
vs
solution
fruits = ["Apple", "Pear", "Orange"]
#TODO: Catch the exception and make sure the code runs without crashing.
def make_pie(index):
try:
fruit = fruits[index]
except IndexError:
print("Fruit pie")
else:
print(fruit + " pie")
make_pie(4)
확실히 솔루션이 이해가 더 잘 된다.
- Key Error Handling
We've got some buggy code, try running the code. The code will crash and give you a KeyError. This is because some of the posts in the facebook_posts don't have any "Likes".
facebook_posts = [
{'Likes': 21, 'Comments': 2},
{'Likes': 13, 'Comments': 2, 'Shares': 1},
{'Likes': 33, 'Comments': 8, 'Shares': 3},
{'Comments': 4, 'Shares': 2},
{'Comments': 1, 'Shares': 1},
{'Likes': 19, 'Comments': 3}
]
total_likes = 0
for post in facebook_posts:
total_likes = total_likes + post['Likes']
print(total_likes)
Use what you've learnt about exception handling to prevent the program from crashing.
facebook_posts = [
{'Likes': 21, 'Comments': 2},
{'Likes': 13, 'Comments': 2, 'Shares': 1},
{'Likes': 33, 'Comments': 8, 'Shares': 3},
{'Comments': 4, 'Shares': 2},
{'Comments': 1, 'Shares': 1},
{'Likes': 19, 'Comments': 3}
]
total_likes = 0
for post in facebook_posts:
try:
total_likes = total_likes + like
except KeyError:
total_likes += 0
print(total_likes)
my try
vs
solution
facebook_posts = [
{'Likes': 21, 'Comments': 2},
{'Likes': 13, 'Comments': 2, 'Shares': 1},
{'Likes': 33, 'Comments': 8, 'Shares': 3},
{'Comments': 4, 'Shares': 2},
{'Comments': 1, 'Shares': 1},
{'Likes': 19, 'Comments': 3}
]
total_likes = 0
for post in facebook_posts:
try:
total_likes = total_likes + like
except KeyError:
pass
print(total_likes)
Exception Handling in the NATO Phonetic Alphabet Project
import pandas
data = pandas.read_csv("nato_phonetic_alphabet.csv")
phonetic_dict = {row.letter: row.code for (index, row) in data.iterrows()}
print(phonetic_dict)
word = input("Enter a word: ").upper()
output_list = [phonetic_dict[letter] for letter in word]
print(output_list)
If someone type numbers instead of a word, it will cause an error.
일단 돌려서 숫자 입력을 해보았더니 KeyError가 발생
word = input("Enter a word: ").upper()
try:
output_list = [phonetic_dict[letter] for letter in word]
except KeyError:
print("Sorry, only letters in the alphabet please.")
else:
print(output_list)
이렇게 하면 KeyError에 대한 피드백을 주어 알파벳을 입력하도록 유도할 수 있다.
알파벳을 제외한 숫자나 심볼을 잘못 입력했을 경우 다시 입력할 수 있도록 할 필요가 있다.
(some kind of a loop)
def generate_phonetic():
word = input("Enter a word: ").upper()
try:
output_list = [phonetic_dict[letter] for letter in word]
except KeyError:
print("Sorry, only letters in the alphabet please.")
generate_phonetic()
else:
print(output_list)
아직 function 안에 function을 불러올 수 있다는 것을 제대로 숙지하지 못해서 해결방법을 찾는 것이 어려웠다.
>>> 나중에 다시 공부해 볼 것 <<<
JSON
JavaScript Object Notation
Write : json.dump()
Read : json.load()
Update : json.update()
Example)
Password Generator Project
Coming back to the Password Generator Project, we're gonna replace some code with json.
previous
def save_info():
website = website_entry.get().title()
email = email_entry.get()
password = password_entry.get()
if len(website) == 0 or len(email) == 0 or len(password) == 0:
messagebox.showinfo(title="Oops", message="Please make sure you haven't left any fields empty")
else:
is_ok = messagebox.askokcancel(title=website, message=f"These are the details entered: \nEmail: {email} "
f"\nPassword: {password} \nIs it okay to save?")
if is_ok:
with open("data.txt", "a") as data_file:
data_file.write(f"{website} | {email} | {password}\n")
website_entry.delete(0, END)
password_entry.delete(0, END)
now
def save():
website = website_entry.get()
email = email_entry.get()
password = password_entry.get()
new_data = {
website: {
"email": email,
"password": password,
}
}
if len(website) == 0 or len(password) == 0:
messagebox.showinfo(title="Oops", message="Please make sure you haven't left any fields empty.")
else:
is_ok = messagebox.askokcancel(title=website, message=f"These are the details entered: \nEmail: {email} "
f"\nPassword: {password} \nIs it ok to save?")
if is_ok:
with open("data.json", "w") as data_file:
json.dump(new_data, data_file, indent=4)
website_entry.delete(0, END)
password_entry.delete(0, END)
Write with json
1. 들어갈 데이터 2. 들어갈 파일 3.보기 쉽게 indent 정렬
with open("data.json", "w") as data_file:
json.dump(new_data, data_file, indent=4)
data.json =
{
"Amazon": {
"email": "angela@gmail.com",
"password": "xMVIJ$2UH(w3xh%"
}
}
Read with json
with open("data.json", "r") as data_file:
data = json.load(data_file)
print(data)
result
{
"Amazon": {
"email": "angela@gmail.com",
"password": "xMVIJ$2UH(w3xh%"
}
}
type(data) = 'dict'
Update with json
with open("data.json", "r") as data_file:
data = json.load(data_file)
data.update(new_data)
Read, Update, and Write
with open("data.json", "r") as data_file:
# Reading old data
data = json.load(data_file)
# Updating old data with new data
data.update(new_data)
with open("data.json", "w") as data_file:
# Saving updated data
json.dump(new_data, data_file, indent=4)
Json으로 read, update, and write.
Handling errors
But there's a problem. If we run it for the first time, and there's no data.json file, it crushes and we get some exceptions being thrown.
def save():
website = website_entry.get()
email = email_entry.get()
password = password_entry.get()
new_data = {
website: {
"email": email,
"password": password,
}
}
if len(website) == 0 or len(password) == 0:
messagebox.showinfo(title="Oops", message="Please make sure you haven't left any fields empty.")
else:
is_ok = messagebox.askokcancel(title=website, message=f"These are the details entered: \nEmail: {email} "
f"\nPassword: {password} \nIs it ok to save?")
if is_ok:
with open("data.json", "r") as data_file:
# Reading old data
data = json.load(data_file)
# Updating old data with new data
data.update(new_data)
with open("data.json", "w") as data_file:
# Saving updated data
json.dump(new_data, data_file, indent=4)
website_entry.delete(0, END)
password_entry.delete(0, END)
Fixing the problem
try:
with open("data.json", "r") as data_file:
# Reading old data
data = json.load(data_file)
except FileNotFoundError:
with open("data.json", "w") as data_file:
# Saving updated data
json.dump(new_data, data_file, indent=4)
else:
# Updating old data with new data
data.update(new_data)
with open("data.json", "w") as data_file:
# Saving updated data
json.dump(new_data, data_file, indent=4)
finally:
website_entry.delete(0, END)
password_entry.delete(0, END)
- Try: 먼저 시도, 에러가 없다면 제일 먼저 실행 (try block can fail)
- Except: try에서 어떤 에러가 발생하고 그 에러가 except의 에러에 해당할 경우 실행 (except block deals with any failures that occur)
- Else: try가 문제 없이 실행되었다면 그 후에 실행 (try block will run if there were no issues)
- Finally: Try든 Except든 관계 없이 마지막에 항상 실행 (finally block will run at the very end no matter if there was an issue or no issue)
Adding Search function to Password Generator
혼자 풀어본 코드
search_button = Button(text="Search", width=13, command=find_password)
search_button.grid(row=1, column=2)
def find_password():
website = website_entry.get()
with open("data.json", "r") as data_file:
data = json.load(data_file)
try:
email = data[website]["email"]
password = data[website]["password"]
except FileNotFoundError:
messagebox.showinfo(title="Oops", message="No data file found.")
except KeyError:
messagebox.showinfo(title="Oops", message="No details for the website exists.")
else:
messagebox.showinfo(title=website, message=f"Emails: {email}\nPassword: {password}")
혼자 풀때는 Key error도 exception으로 만들었지만
빈번하게 발생하는 일 혹은 if and else statement로 해결 가능한 일은 exception을 쓰기 보다는 if and else statement를 쓰는게 더 좋다.
Exception은 더 쉽게 대체할만한 방법이 없을때 쓸 것
Solution
def find_password():
website = website_entry.get()
try:
with open("data.json", "r") as data_file:
data = json.load(data_file)
except FileNotFoundError:
messagebox.showinfo(title="Oops", message="No data file found.")
else:
if website in data:
email = data[website]["email"]
password = data[website]["password"]
messagebox.showinfo(title=website, message=f"Emails: {email}\nPassword: {password}")
else:
messagebox.showinfo(title="Oops", message=f"No details for the {website} exists.")
마지막으로 Password Generator update하는 부분은 어렵지만 재미있었다.
수업을 들으면서 배우고 코드 써보고 하는 과정은 즐겁지만 복습하지 않으면 금방 잊어버리게 된다.
다음 수업인 day 31이 intermediate - 의 마지막인데 다음 수업까지 듣고 Intermediate 강의를 한번씩 복습하는 시간을 가지면서 배웠던 내용을 한번씩 더 공부하고 사용해보는 시간을 가지는 것이 좋을 것 같다.