Python Coding Style & Error Handling¶

資訊之芽 梁安哲 2023/04/23

Outline¶

  • Coding Style
  • Error Handling
  • Common Pitfalls

Coding Style¶

Example¶

你是一位任職於金融公司的電腦工程師,你的上司要你製作一個電腦程式,這個電腦程式的輸入為一支股票的歷史股價,輸出為這支股票的股價在什麼時候處於相對高點(比前一天與後一天都高)。

Your Solution¶

Bad Solution¶

In [11]:
targetArray = [1,4,7,2,8,10,4]
f = lambda x: [        1 if x[a]>x[a-1] and x[a]>x[a+1]  else 0 for a in range(1, len(x)-1)]
print(f(targetArray))
[0, 1, 0, 0, 1]

Instructor's Solution¶

In [12]:
def checkIfExtreme(index , array):
    if array[index] > array[index-1] and array[index] > array[index+1]:
        return 1
    else:
        return 0
    
def getAllExtreme(array):
    result = []
    for index in range(len(array)):
        if index == 0 or index == len(array)-1:
            # prevent Index Error
            continue
        else:
            result.append(checkIfExtreme(index , array))
    return result

print(getAllExtreme(targetArray))
[0, 1, 0, 0, 1]

PEP8: Style Guide for Python Code¶

為了讓別人更好的讀懂你的程式,大家約定了一套 Python 程式的撰寫指南 link。

不遵守程式說不定還是可以跑,只是大家會覺得你寫一堆屎 code 。

Indentation¶

  1. 使用 4 個空格
  2. 換行對齊
In [17]:
var_one = "sprout"
var_two = 2023
var_three = ["Python", "Taipei"]
var_four = False
# Correct:

# Add 4 spaces (an extra level of indentation) to distinguish arguments from the rest.

def long_function_name(
        var_one, var_two, var_three,
        var_four):
    print(var_one)


# Aligned with opening delimiter.
foo = long_function_name(var_one, var_two,
                         var_three, var_four)
sprout

Space¶

In [18]:
i = 1
submitted = 3
x = 8
y = 6
a = 4
b = 42
i = i + 1
submitted += 1
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)
print(i, submitted, x, y, hypot2, a, b, c)
2 4 15 6 261 4 42 -1748

Import¶

In [ ]:
# Correct:
import os
import sys
from subprocess import Popen, PIPE

Naming Conventions¶

好的變數/函式/類別名稱可以讓別人/未來的自己更容易讀懂現在的程式。

  • 僅限使用英文字母與底線(數字)
  • 名稱 = 變數存了什麼資料 / 函式做了什麼事
    • student_scores 好於 data
    • get_distance(x,y) 好於 process(x,y)
  • 若變數型態為boolean,可以嘗試以is_、has_為開頭。e.g. is_even
  • 命名函數時以動詞為單位。e.g. download_from_url(url)
  • 命名類別時以抽象意義為單位。 e.g GameObject->Character->Player
  • 命名慣例
    • Snakecase: variable_one(常見於Python)
    • Pascalcase: VariableOne(常見於Java)
    • Camelcase: variableOne
    • Hungarian Notation: (常見於C#)
      • arrDistrubuteGroup Array called "Distribute Group"
      • sUserName String called "User Name"
      • iRandomSeed Integer called "Random Seed"

autopep8¶

https://pypi.org/project/autopep8/

View->Command Palette->Format Code

Error Handling¶

在程式執行時,有可能遇到各種不可預期的錯誤:

  1. 除法時以 0 為底數
  2. 資料型態錯誤
  3. 存陣列的元素超出範圍
  4. 遞迴太多次把 Stack 塞滿
  5. 連不上網路(二階會學到)
  6. 找不到檔案
  7. 有一隻飛蛾飛到你的主機板裡面
  8. 電腦被宇宙射線打到

理想上,你撰寫的程式應該要能處理這些錯誤,而不是直接強制停止。

In [1]:
number_of_student = 0
total_score = 200
average_score = total_score / number_of_student
print(average_score)
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
Cell In[1], line 3
      1 number_of_student = 0
      2 total_score = 200
----> 3 average_score = total_score / number_of_student
      4 print(average_score)

ZeroDivisionError: division by zero
In [3]:
array = [1, 2, 3]
next_number = 4
print(array + next_number)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[3], line 3
      1 array = [1, 2, 3]
      2 next_number = 4
----> 3 print(array + next_number)

TypeError: can only concatenate list (not "int") to list

Try except¶

Syntax¶

In [6]:
number_of_student = 0
total_score = 200
try:
    average_score = total_score / number_of_student
    print(average_score)
except:
    print("Something went wrong!")
print("The program is still running")
Something went wrong!
The program is still running
In [12]:
number_of_student = 0
total_score = 200
try:
    average_score = total_score / number_of_student
    print(average_score)
except ZeroDivisionError:
    print("Can't devide by zero!")
except IndexError:
    print("List index out of range!")
print("The program is still running")
Can't devide by zero!
The program is still running
In [13]:
number_of_student = 0
total_score = 200
try:
    average_score = total_score / number_of_student
    print(average_score)
except FileNotFoundError:
    print("Can't find file")
print("The program is still running")
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
Cell In[13], line 4
      2 total_score = 200
      3 try:
----> 4     average_score = total_score / number_of_student
      5     print(average_score)
      6 except FileNotFoundError:

ZeroDivisionError: division by zero

Some Errors¶

  • IndexError
  • KeyError
  • ArithmeticError
  • RecursionError
  • TypeError
  • SyntaxError & IndentationError try-except 抓不到
  • PermissionError & ChildProcessError 跟作業系統有關

Know Your Error¶

In [ ]:
try:
    array = [1, 2, 3]
    print(array[20])
except Exception as e:
    print(e, type(e))
list index out of range <class 'IndexError'>

finally¶

In [15]:
number_of_student = 0
total_score = 200
done_calculation = False
try:
    average_score = total_score / number_of_student
    print(average_score)
except ZeroDivisionError:
    print("Can't devide by zero!")
finally:
    done_calculation = True
print(f"The program is still running, done calculation={done_calculation}")
Can't devide by zero!
The program is still running, done calculation=True

raise¶

In [16]:
students_weight = [60, 50, 75, -20, 80]
for weight in students_weight:
    if weight <= 0:
        raise ValueError("Weight can't be < 0")
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[16], line 4
      2 for weight in students_weight:
      3     if weight <= 0:
----> 4         raise ValueError("Weight can't be < 0")

ValueError: Weight can't be < 0

assert¶

In [4]:
n = 20
assert n > 100, "x is smaller than 100"
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
Cell In[4], line 2
      1 n = 20
----> 2 assert n > 100, "x is smaller than 100"

AssertionError: x is smaller than 100

Common practice: use assert in development, raise in production.

Practice¶

試著用 try-except 重寫一次作業 3036,同時要使用好的變數名稱以及程式結構。

嘗試把程式寫對之外,還要易於解釋。(可以講給你隔壁的同學聽,順便找第二階段大作業的組員)

Instructor's Solution¶

哈哈這次沒解答 :)

Common Pitfalls / Best Practice¶

Logical (https://neoj.sprout.tw/problem/3109/)¶

In [ ]:
n = int(input())
for i in range(n):
    word = input()
    L = len(word)
    if word.isalpha():
        new_word = word.lower()
        if new_word[0:int(L/2):1] == new_word[-1:int(L/2):-1]:
            print("Za Warudo!!")
        else:
            print("ROADO ROLLA DA!!")
    else:
        text = []
        for i in word:
            if i.isalpha():
                j = i.lower()
                text.append(j)
            else:
                pass
        new_L = len(text)
        
        if text == text[::-1]:
            print("Za Warudo!!")
        else:
            print("ROADO ROLLA DA!!")

Naming Variables (https://neoj.sprout.tw/problem/3036/)¶

In [ ]:
n=int(input())
dict={}
for i in range(n):
    x=input().split()
    if x[0]=="1":
        if x[1] in dict:
            print("Failed")

        else:
            dict[x[1]]=x[2]
            print("Success")

    elif x[0]=="2":
        if x[1] in dict:
            dict.pop(x[1])
            print("Success")
        else:
            print("Failed")

    elif x[0]=="3":
        if len(dict)==0:
            print("Failed")
        else:
            dict.popitem()
            print("Success")

    elif x[0]=="4":
        if x[1] in dict:
            print(dict[x[1]])
        else:
            print("Failed")

    elif x[0]=="5":
        a=0
        for  i in dict.keys():
            if x[1] == dict[i]:
                print(i)
                a=3
        if a==0:
            print("None")

    elif x[0]=="6":
        if x[1] in dict:
            dict[x[1]]=x[2]
            print("Success")
        else:
            print("Failed")

    elif x[0]=="7":
        if len(dict)>0:
            k=list(dict.items())
            k.sort()
            for i in k:
                print(*i)

    elif x[0]=="8":
        print(len(dict))

    elif x[0]=="9":
        dict.clear()

Separation of concerns (https://neoj.sprout.tw/problem/3108/)¶

In [ ]:
def average(lst):
    return sum(lst) / len(lst)
def stddev(lst):
    ave = average(lst)
    return (sum((x - ave)**2 for x in lst) / len(lst))**.5
students = {}
subjects = {}
n = int(input())
for _ in range(n):
    name, subject, score = input().split()
    score = float(score)
    students.setdefault(name, {})
    students[name][subject] = score
    subjects.setdefault(subject, {})
    subjects[subject][name] = score
q = int(input())
for _ in range(q):
    s = input().split()
    op = s[0]
    if op == '1':
        name = s[1]
        subject = s[2]
        print(round(students[name][subject], 2))
    elif op == '2':
        name = s[1]
        scores = students[name].values()
        print(round(average(scores), 2))
    elif op == '3':
        subject = s[1]
        scores = subjects[subject].values()
        print(round(average(scores), 2), round(stddev(scores), 2))
    elif op == '4':
        subject = s[1]
        r = int(s[2])
        lst = []
        for tu in subjects[subject].items():
            lst.append((tu[1], tu[0]))
        lst.sort(reverse=True)
        print(lst[r-1][1])
    elif op == '5':
        r = int(s[1])
        lst = []
        for item in students.items():
            lst.append((average(list(item[1].values())), item[0]))
        lst.sort(reverse=True)
        print(lst[r-1][1])
    elif op == '6':
        for name in sorted(list(students.keys())):
            print(name, end='')
            dic = students[name]
            for subject in sorted(list(dic.keys())):
                print(' {}:{}'.format(subject, round(dic[subject], 2)), end='')
            print()

Commenting¶

In [ ]:
n= int(input())
listowo={}
count=0
documentta=[]
for i in range(n):
    inputs=input().split()
    op=int(inputs[0])
    if op==1:
        #插入key a,value為b 。如果已經存在key ,就印出"Failed"就好(不用更改value),不然插入後印出"Success"。
        key1=inputs[1]
        if key1 in listowo:
            print("Failed")
        else:
            listowo[key1] = inputs[2]
            print("Success")
            documentta.insert(count, key1)
            #print(documentta)
    elif op==2:
        # 刪除key 和他對應的value。如果key 不存在,印出"Failed",不然刪除後印出"Success"。
        key1=inputs[1]
        if key1 not in listowo:
            print("Failed")
        else:
            listowo.pop(key1)
            print("Success")
            if key1 in documentta:
                documentta.remove(key1)
            #print(documentta)
    elif op==3:
        #在目前還存在的key中刪除最晚插入的那一個。如果dict是空的,印出"Failed",不然刪除後印出"Success"。
        if documentta==[]:
            print("Failed")
        else:
            listowo.pop(documentta[-1])
            del documentta[-1]
            print("Success")
    elif op==4: #目前只debug到第三行
        # 印出key a對應到的value。如果key a 不存在,印出"Failed"。
        key1=inputs[1]
        if key1 not in listowo:
            print("Failed")
        else:
            print(listowo[key1])
    elif op==5:
         #照字典序印出所有value為a的key,每行一個。如果沒有任何一個key的value是 a,印出"None"。注意到是"None",不是"Failed”哦。
        key1=inputs[1]
        if key1 not in listowo.values():
            print("None")
        else:
            for i in listowo:
                if listowo[i]==key1:
                    print(i)
    elif op==6:
        # ab將key為a對應到的value改成b。如果key a不存在,印出"Failed",不然改完後印出"Success"。
        key1=inputs[1]
        if key1 not in listowo:
            print("Failed")
        else:
            listowo[key1]=inputs[2]
            print("Success")
    elif op==7:
        #照key的字典序印出所有的"key value"(中間空白),每行一組
        ans=list(listowo.items()) #如果像提示一樣ans=[list1.items()]會將list1.items(dict)放進list 中,但是我們是要將
            #這個變成list
        ans.sort() # 把(key, value)當成tuple放進來
        for key, value in ans:
            print(key, value)
    elif op==8:
        print(len(listowo))
    elif op==9:
        listowo.clear()
        documentta=[]
    count+=1

恭喜,你的 Code 自己也看得懂了。¶