Python 程式設計入門 - 06

Pyladies Taiwan

Speaker : Mars

2017/12/17

Roadmap

  • 前情提要:迴圈
  • 函式
  • 可視範圍
  • 排序
  • 解題時間
  • 補充知識:參數
  • 補充知識:插入排序法

前情提要:迴圈

  • 迴圈可以處理重複的事情
  • 迴圈可以很方便處理可迭代的物件
    • 串列
    • 字串
    • 字典
    • ......

輸入三個數字,加到串列裡面

l = []
l.append(input())
l.append(input())
l.append(input())
print(l)

用迴圈可以改寫為

l = []
for i in range(3):
    l.append(input())
print(l)

拿取串列各元素

l = [1,4,7,9]
print(l[0])
print(l[1])
print(l[2])
print(l[3])

用迴圈可以改寫為

l = [1,4,7,9]
for i in range(0,3+1):
    print(l[i])

或是改寫成更為簡潔的

l = [1,4,7,9]
for i in l:
    print (i)

上面的程式碼執行起來的概念等同於

i=1  # 串列l的第1個元素
print(i)
i=4  # 串列l的第2個元素
print(i)
i=7  # 串列l的第3個元素
print(i)
i=9  # 串列l的第4個元素
print(i)

range迭代,雖然寫起來比較複雜,但彈性比較高
(eg.只想拿取奇數位置的元素)

l = [1,4,7,9]
for i in range(0,3+1):
    print(l[i])

以串列迭代,雖然寫起來比較簡潔,但只能所有元素全部拿取
(eg.只想拿取奇數位置的元素,要另外在迴圈裡寫判斷式)

l = [1,4,7,9]
for i in l:
    print (i)

函式

# 定義函式
def 函式名稱(參數1,參數2,...):
    程式碼
    程式碼
    程式碼
    return 物件1,物件2,物件3
# 呼叫函式
函式名稱(引數1,引數2,...)
  • 參數(patameter)是定義函式時指定的名稱,可有可無
  • 引數(argumwnt)是函數呼叫時傳入的真正物件,在呼叫時會建立參數和引數的綁定關係。
    • 需和參數的個數一樣
  • 函式內的程式碼可以有各種可能(eg. 迴圈、判斷式、運算...)
  • return回傳可有可無(若無會回傳None),用tuple可回傳多個物件
  • 自己打造工具
  • 利用呼叫,方便重複利用
    • 程式碼更為簡潔
def 洗衣機():
    注水
    旋轉清洗
    排水
    旋轉脫水

  • 去咖啡機弄一杯咖啡
  • 打開開關
  • 放入杯子
  • 選擇「咖啡類別」,按下製作
  • 咖啡機「製作咖啡」
  • 得到一杯咖啡
  • def coffee_machine(coffie_type):
        coffee = espresso
        if coffie_type=="美式咖啡":
            coffee+= water
        elif coffie_type=="拿鐵":
            coffee+= milk*2 + foam
        elif coffie_type=="卡布奇諾":
            coffee+= milk + foam
        .
        .
        .
        return coffee
    cup = coffee_machine("拿鐵")
    

    Recall

    還記得我們使用過不少工具:print()ord()max()...,
    這些都是內建函式Bult-in Function,他們背後的運作原理可能像是這樣:

    In [1]:
    print(max([1,2,7,9,10]))
    a=max([1,2,7,9,10])
    print(a)
    
    10
    10
    
    In [2]:
    def my_max(data):
        num_max = -999999999
        for i in data:
            if i>=num_max:
                num_max = i
        return num_max
    print(my_max([1,2,7,9,10]))
    
    10
    

    練習-自己寫一個Function

    判斷是否為閏年:

    • 西元年可被4整除,但不能被100整除
    • 西元年份除以400可整除

    答案

    • 閏年:2000、2400
    • 平年:1800、1900、2100、2200、2300、2500

    Hint:

    • 判斷式:if、elif、else
    • a%b:可以取a除以b的餘數
    def is_leap(year):
        leap = False
        ...
        你的程式碼
        ...
        return leap
    
    print(is_leap(2000))
    print(is_leap(1800))
    

    簡化程式碼,方便重複利用

    In [3]:
    l = [["姿君",25],["Mars",20],["毛毛",10]]
    for member in l:
        if member[0]=="Mars":
            member[1]+=15
    for member in l:
        if member[0]=="姿君":
            member[1]+=5
    print(l)
    
    [['姿君', 30], ['Mars', 35], ['毛毛', 10]]
    
    In [4]:
    l = [["姿君",25],["Mars",20],["毛毛",10]]
    def change_money(name,money):
        for member in l:
            if member[0]==name:
                member[1]+=money     
    change_money("Mars",15)
    a = change_money("姿君",5) # 這是一個沒有回傳值的函式
    print(a)
    print(l)
    
    None
    [['姿君', 30], ['Mars', 35], ['毛毛', 10]]
    

    練習-使用Function

    目前程式中記錄著會員跟儲值金額, 已經寫好一個自行定義的Function:change_money
    有一個大家加值記錄的文檔log,請大家處理這份文檔,加值到程式中

    答案是 [['姿君', 50], ['Mars', 35], ['毛毛', 20]]

    l = [["姿君",25],["Mars",20],["毛毛",10]]
    def change_money(name,money):
        for member in l:
            if member[0]==name:
                member[1]+=money  
    log = """
    Mars 15
    姿君 5
    姿君 20
    毛毛 10
    """  
    ...
    你的程式碼
    ...
    print(l)
    

    Hint:

    函式可以當字典的value

    • 函式也是物件(Python3中所有東西都是物件)
    In [5]:
    def f1():
        print("f1")
    def f2():
        print("f2")
    def f3():
        print("f3")
    
    d = {"apple":f1, "banana":f2, "orange":f3}
    d["apple"]()
    
    f1
    

    可視範圍scope

    • 第一次指派名稱,決定了該名稱位於哪個範圍
    • 區域local > 全域global > 內建bult-in
      • 找尋名稱在哪個範圍,越大的優先
      • 在不同範圍內有名稱同名,越大的會掩蓋掉其他的
    • 函式的區域範圍各自獨立
    • 迴圈、判斷式 不是獨立的區域範圍

    區域local > 全域global > 內建built-in

    In [6]:
    b = 2                       # global
    def test():
        a = 1                   # local
        print(a,b,len([3,4,5])) # local,global,built-in
    test()
    
    1 2 3
    
    In [7]:
    b = 2                        # global
    def test(a):                 # local 
        print(a,b,len([3,4,5]))  # local,global,built-in
    test(1)
    
    1 2 3
    

    注意:指派前就存取

    雖然Python我們通常理解為一行行執行,
    但在遇到函式時,他解析函式

    In [8]:
    b = 2                        # global
    def test(a):                 # local    
        print(a,b)               # local,global?
        b = a+1                  # b=a+1 :指派名稱 b 給a+1 => 所以 b 是 local
    test(1)
    
    ---------------------------------------------------------------------------
    UnboundLocalError                         Traceback (most recent call last)
    <ipython-input-8-808bd44f8e1a> in <module>()
          3     print(a,b)               # local,global?
          4     b = a+1                  # b=a+1 :指派名稱 b 給a+1 => 所以 b 是 local
    ----> 5 test(1)
    
    <ipython-input-8-808bd44f8e1a> in test(a)
          1 b = 2                        # global
          2 def test(a):                 # local
    ----> 3     print(a,b)               # local,global?
          4     b = a+1                  # b=a+1 :指派名稱 b 給a+1 => 所以 b 是 local
          5 test(1)
    
    UnboundLocalError: local variable 'b' referenced before assignment

    在不同範圍內有名稱同名

    In [9]:
    a,b = 1,2                             # global
    def test(a,b):
        len = 6                           # local
        print(a,b,len,"in function test") # local,local,local
    test(3,4)
    c = len([7,8,9])                      # built-in
    print(a,b,c)                          # global,global,global
    
    3 4 6 in function test
    1 2 3
    
    In [10]:
    a,b = 1,2                             # global
    def test(a,b):
        len = 6                           # local 
        print(a,b,len,"in function test") # local,local,local
        print(len([7,8,9])) # error 因為 local 掩蓋掉 built-in
    test(3,4)
    
    3 4 6 in function test
    
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-10-072a24760387> in <module>()
          4     print(a,b,len,"in function test") # local,local,local
          5     print(len([7,8,9])) # error 因為 local 掩蓋掉 built-in
    ----> 6 test(3,4)
    
    <ipython-input-10-072a24760387> in test(a, b)
          3     len = 6                           # local
          4     print(a,b,len,"in function test") # local,local,local
    ----> 5     print(len([7,8,9])) # error 因為 local 掩蓋掉 built-in
          6 test(3,4)
    
    TypeError: 'int' object is not callable

    函式的區域範圍各自獨立

    In [11]:
    a,b = 1,2
    def test(a,b):
        print(a,b,"in function test")    
    def temp(a,b):
        print(a,b,"in function temp")    
    test(3,4)
    temp(5,6)
    
    3 4 in function test
    5 6 in function temp
    

    那要怎麼存取跨函式間的物件呢?

    def change_money(name,money):
        for member in l:
            if member[0]==name:
                member[1]+=money      
    def main():
        l = [["姿君",25],["Mars",20],["毛毛",10]]
        change_money("Mars",15)
        change_money("姿君",5)
        print(l)
    
    main()
    

    使用全域名稱

    • 有可能多個函式都可以對全域物件做修改,debug的時候要特別注意
      • 可用__當作名稱前綴,方便維護 eg. __my_list
    In [12]:
    # 直接把物件宣告在全域
    l = [["姿君",25],["Mars",20],["毛毛",10]] 
    def change_money(name,money):
        for member in l:
            if member[0]==name:
                member[1]+=money      
        # 這裡面沒有重新指派名稱l,所以用的就是global的l 
    def main():
        change_money("Mars",15)
        change_money("姿君",5)
        print(l)
    
    main()
    
    [['姿君', 30], ['Mars', 35], ['毛毛', 10]]
    
    In [13]:
    # 利用 global 把物件變成全域
    def change_money(name,money):
        for member in l:
            if member[0]==name:
                member[1]+=money      
    def main():
        global l  # 在使用前先宣告
        l = [["姿君",25],["Mars",20],["毛毛",10]] # global l
        change_money("Mars",15)
        change_money("姿君",5)
        print(l)
    
    main()
    
    [['姿君', 30], ['Mars', 35], ['毛毛', 10]]
    

    使用參數傳遞

    • 可變物件在函式中處理後,還是原本的物件
    • 不可變物件修改後是一個全新的物件
    In [14]:
    # 可變物件
    def change_money(name,money,l):
        print (id(l),"in function origin")
        for member in l:
            if member[0]==name:
                member[1]+=money
        print (id(l),"in function after change")
    def main():
        l = [["姿君",25],["Mars",20],["毛毛",10]] 
        print (id(l))
        change_money("Mars",15,l)
        change_money("姿君",5,l)
        print(l)
    
    main()
    
    139675831776648
    139675831776648 in function origin
    139675831776648 in function after change
    139675831776648 in function origin
    139675831776648 in function after change
    [['姿君', 30], ['Mars', 35], ['毛毛', 10]]
    
    In [15]:
    # 不可變物件
    def add(x,y):
        print(id(x),id(y),"in function origin")
        x = x+1
        y = y+2
        print(id(x),id(y),"in function after change")
        print(x,y)
    def main():
        x = 5
        y = 10
        print(id(x),id(y))
        add(x,y)
        print(x,y)
        
    main()
    
    10105952 10106112
    10105952 10106112 in function origin
    10105984 10106176 in function after change
    6 12
    5 10
    
    In [16]:
    # 不可變物件
    def add(x,y):
        x = x+1
        y = y+2
        print(x,y)
        return x,y # 要記得回傳
    def main():
        x = 5
        y = 10
        x,y = add(x,y)
        print(x,y)
        
    main()
    
    6 12
    6 12
    

    迴圈、判斷式 不是獨立的區域範圍

    In [17]:
    for i in range(1,5+1):
        a=10
    print(i,a)
    
    5 10
    
    In [18]:
    # 所以如果你的物件名稱在迴圈中有用到,小心被修改到
    i=1   
    for i in range(1,5+1):
        a=10
    print(i,a)
    
    5 10
    

    排序

    請告訴我出現最多的字母

    In [19]:
    s = "g fmnc wms bgblr rpylqjyrc gr zw fylb. rfyrq ufyr amknsrcpq ypc dmp. bmgle gr gl zw fylb gq glcddgagclr ylb rfyr'q ufw rfgq rcvr gq qm jmle. sqgle qrpgle.kyicrpylq() gq pcamkkclbcb. lmu ynnjw ml rfc spj."
    unique_word_list=[]
    max_count = 0
    max_alpha = ""
    for i in s:
        if (ord(i) in range(ord("a"),ord("z")+1)) and (i not in unique_word_list):
            unique_word_list.append(i)
            # print (i,s.count(i))
            if s.count(i) > max_count:
                max_count = s.count(i)
                max_alpha = i
    print("{}是出現最多的字母,出現了{}次".format(max_alpha,max_count))
    
    r是出現最多的字母,出現了18次
    

    那如果想要知道出現次數前五名的? 難道要寫max_count1,max_count2,max_count3...?
    這時,排序就是個好方法,雖然會多花一點排序的計算資源,但排序後的應用非常值得!
    (圖書館如果都不花時間整理書,相信借書的時候找書一定找得很辛苦)

    內建函式 sorted(序列,key,reverse)

    • 回傳一個排序好的串列

    排序方式

    • 若物件本身為序列,則先以索引小的元素比對
    • 關鍵字參數
      • key:排序依據的函式
      • reverse:預設為False將物件由小到大排序,True則是由大到小
    In [20]:
    simple_list = (1,5,3,4,2)
    print(sorted(simple_list))
    print(sorted(simple_list,reverse=True))
    print(simple_list)
    
    [1, 2, 3, 4, 5]
    [5, 4, 3, 2, 1]
    (1, 5, 3, 4, 2)
    
    In [21]:
    nested_list_number = [[5,4],[1,9],[1,7],[4,2]]
    nested_list_string = [["Python",4],["Hello",6],["World",2],["PyLadies",2]]
    print(sorted(nested_list_number))
    print(sorted(nested_list_string))
    
    [[1, 7], [1, 9], [4, 2], [5, 4]]
    [['Hello', 6], ['PyLadies', 2], ['Python', 4], ['World', 2]]
    

    可變序列.sort(key,reverse)

    • 可變序列的方法都是會原地修改自己,所以回傳值會為None
    • 速度比內建函式sorted()快,但只支援可變序列

    排序方式

    • 若物件本身為序列,則先以索引小的元素比對
    • 關鍵字參數
      • key:排序依據的函式
      • reverse:預設為False將物件由小到大排序,True則是由大到小
    In [22]:
    simple_list = [1,5,3,4,2]
    print(simple_list.sort())
    print(simple_list)
    
    None
    [1, 2, 3, 4, 5]
    
    In [23]:
    simple_list = [1,5,3,4,2]
    simple_list.sort(reverse=True)
    print(simple_list)
    
    [5, 4, 3, 2, 1]
    

    練習-取得中位數

    • 將元素從小到大排列
    • 若元素為奇數個,則取最中間那個值
    • 若元素為偶數個,則取中間那兩個值的平均值

    答案

    • 12,4,5,3,8,7,排序後為3,4,5,7,8,12,中位數為(5+7)/2 = 6.0
    • 12,4,5,3,8,排序後為3,4,5,8,12,中位數為5

    Hint:

    • Python串列索引是從0開始
    • 索引要為整數,但除法商為小數,需要做型別轉換:6/2 = 3.0
    • 可以嘗試寫成函式
    def get_median(l):
        排序
        if len(l)%2==1: 
            return ...
        else:
            return ...
    
    print(get_median([12,4,5,3,8,7]))
    print(get_median([12,4,5,3,8]))
    

    自訂排序

    如果想要以第二個元素去排...

    In [24]:
    def my_sort(item):
        return item[1]
    
    def my_sort2(item):
        return item[1],item[0] # 如果第二個元素一樣,則以第一個元素排序
    
    nested_list_string = [["Python",4],["Hello",6],["World",2],["PyLadies",2]]
    print(sorted(nested_list_string))
    print(sorted(nested_list_string,key=my_sort))
    print(sorted(nested_list_string,key=my_sort2))
    
    [['Hello', 6], ['PyLadies', 2], ['Python', 4], ['World', 2]]
    [['World', 2], ['PyLadies', 2], ['Python', 4], ['Hello', 6]]
    [['PyLadies', 2], ['World', 2], ['Python', 4], ['Hello', 6]]
    
    In [25]:
    # 想要以第二個元素由大到小排序,如果相同則以第一個元素由小到大排序
    def my_sort(item):
        return item[1]
    
    def my_sort2(item):
        return -item[1],item[0] # 因為第二個元素剛好是數字,可以用負數處理
    
    nested_list_string = [["Python",4],["Hello",6],["World",2],["PyLadies",2]]
    print(sorted(nested_list_string,key=my_sort,reverse=True))
    print(sorted(nested_list_string,key=my_sort2))
    
    [['Hello', 6], ['Python', 4], ['World', 2], ['PyLadies', 2]]
    [['Hello', 6], ['Python', 4], ['PyLadies', 2], ['World', 2]]
    

    字典排序

    利用字典.items()取得所有鍵值與值的配對

    In [26]:
    def sort_by_value(item):
        return item[1]
    d = {"Python":4, "Hello":6, "World":2, "PyLadies":2}
    print(sorted(d.items())) # items 第一個元素是鍵值key,所以預設以key排序
    print(sorted(d.items(),key=sort_by_value)) 
    
    [('Hello', 6), ('PyLadies', 2), ('Python', 4), ('World', 2)]
    [('PyLadies', 2), ('World', 2), ('Python', 4), ('Hello', 6)]
    

    剛使用排序時, 發覺我們要指定排序算法、是否要翻轉排序的時候,

    不是把我們的參數傳進去就好
    sorted(nested_list_string,my_sort,True)

    需要寫key=reverse=
    sorted(nested_list_string,key=my_sort,reverse=True)

    這種用法叫做「關鍵字參數」,可參考補充知識

    解題時間 Online Judge

    覺得之前練習題太少,很想要再多一些Python基礎語法的練習嗎?

    • 物件、型別轉換:可以拿來儲存東西,好的命名方式很重要
    • 常見的資料型別:字串、串列、字典、集合,這些都有很多好用的方法
    • 判斷式、迴圈:程式中最重要的邏輯
    • 函式:自定義工具、寫更漂亮的程式碼

    以下提供幾個有趣的Python練習網站,也稍微選了一些可練習的題目參考(當然還有更多)

    • HackerRank:介面友善且題目簡單有提示容易上手,但缺點是全英文
    • CheckIO:部分題目有簡中說明,介面較花俏,有些題目會有提示
    • LeetCode:全英文,知名的軟體工程師面試題庫,所以會給不少限制,
      缺點是所有類型的程式碼題目會混雜在一起。

    下一期的活動內容:網路爬蟲、Python 自動化的樂趣,
    會帶領大家學習到更多的Python實戰經驗

    CheckIO

    題目

    CheckIO是利用呼叫函式,得到回傳值來測試答案,
    所以請把程式碼寫在函式裡面,記得都要有回傳值,
    也不要修改函式的名稱、傳遞的參數數量、回傳的參數數量。

    • Run是測試,如果程式有問題會有紅色訊息警示
    • Check是送交程式碼,若成功右方會出現成功畫面

    HackerRank

    題目:

    Python語法練習這幾個章節的題目大家都可以嘗試

    當然 HackerRank 也有更難的Challenges題目,需要考慮效能

    FIND THE RUNNING MEDIAN - 演算法例:會跑的中位數~改變人生從解題開始~ by 五倍紅寶石 tsw

    HackerRank是利用input()輸入資料,然後看print()的結果來驗證答案,
    所以有input()的地方請不要改掉,
    請將你的結果print出來,但請不要多print除了答案以外的東西。

    • Run是測試,如果程式沒問題就會出現綠色勾勾
    • Submit Code是送交程式碼

    有些可以直接寫在全域區域中(記得要縮排)

    有些規定要用函式撰寫 成功畫面

    LeetCode

    題目:

    LeetCode大部分是利用呼叫函式,得到回傳值來測試答案,
    所以請把程式碼寫在函式裡面,記得都要有回傳值,
    也不要修改函式的名稱、傳遞的參數數量、回傳的參數數量。 (但有些會是原地修改,Run Code時會有提示)

    • Run Code是測試,請自行比對看自己的程式答案跟正確答案是否一樣
    • Submit Solution送交程式碼

    測試結果 成功畫面

    補充知識-參數

    基本原理:

    • 參數數量要一致
    • 參數不能重複指定
    • 位置參數、關鍵字參數、形式參數有固定的排序

    關鍵字參數

    • 會先以位置參數為準,然後再放關鍵字參數
    In [27]:
    def f(x,y,z):
        print(x,y,z)
    
    f(1,2,3)        # 位置參數
    f(1,z=3,y=2)    # 關鍵字參數,可不管順序
    
    1 2 3
    1 2 3
    

    參數數量需要一致

    In [28]:
    def f(x,y,z):
        print(x,y,z)
    f(1,2,3,4)
    
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-28-525f5b13dc35> in <module>()
    ----> 1 f(1,2,3,4)
    
    TypeError: f() takes 3 positional arguments but 4 were given
    In [29]:
    f(1,2)
    
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-29-c0c95413393f> in <module>()
    ----> 1 f(1,2)
    
    TypeError: f() missing 1 required positional argument: 'z'

    參數不能重複指定

    In [30]:
    def f(x,y,z):
        print(x,y,z)
    f(1,z=3,x=1,y=2)
    
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-30-dd5e31b9a68b> in <module>()
    ----> 1 f(1,z=3,x=1,y=2)
    
    TypeError: f() got multiple values for argument 'x'

    關鍵字參數後不能有位置參數

    In [31]:
    def f(x,y,z):
        print(x,y,z)
    f(z=3,y=2,1)
    
      File "<ipython-input-31-b1c1fc708b60>", line 1
        f(z=3,y=2,1)
                 ^
    SyntaxError: non-keyword arg after keyword arg
    

    預設值參數

    • 不足的參數會由預設值提供
    In [32]:
    def f(x,y,z=3):
        print(x,y,z)
    f(1,2)
    f(1,2,4)
    
    1 2 3
    1 2 4
    

    和關鍵字參數搭配的優點

    • 使函式呼叫變得更清楚易懂
      remainder(20, 7) v.s. remainder(number=20, divisor=7)
    • 可在函式中指定預設值,以此消除重複的程式碼、降低雜訊
    • 能輕易地擴充一個函式的參數,並保持對舊有函式呼叫的相容性

      參考2016年毛毛帶的Effective Python讀書會

    可在函式中指定預設值,以此消除重複的程式碼、降低雜訊

    In [33]:
    def book_restaurant_for_dinner(location, hour=18):
        print("Book '{}' successfully at {} PM.".format(location, hour))
    
    book_restaurant_for_dinner("Maomao Pasta")
    book_restaurant_for_dinner("Big Hamberger")
    book_restaurant_for_dinner("BBQ Shop", hour=21)
    
    Book 'Maomao Pasta' successfully at 18 PM.
    Book 'Big Hamberger' successfully at 18 PM.
    Book 'BBQ Shop' successfully at 21 PM.
    

    能輕易地擴充一個函式的參數,並保持對舊有函式呼叫的相容性

    In [34]:
    def book_restaurant_for_dinner(location, hour=18, minute=0):
        print("Book '{}' successfully at {}:{:0>2d} PM.".format(location, hour, minute))
    
    # 舊函式呼叫 (不需修改)
    book_restaurant_for_dinner("Maomao Pasta")
    book_restaurant_for_dinner("Big Hamberger")
    book_restaurant_for_dinner("BBQ Shop", hour=21)
    
    # 新函式呼叫
    book_restaurant_for_dinner("Abby Pasta", minute=30)
    
    Book 'Maomao Pasta' successfully at 18:00 PM.
    Book 'Big Hamberger' successfully at 18:00 PM.
    Book 'BBQ Shop' successfully at 21:00 PM.
    Book 'Abby Pasta' successfully at 18:30 PM.
    

    預設值參數後面不能有非預設值參數

    In [35]:
    def f(x,y=2,z):
        print(x,y,z)
    f(1,z=3)
    
      File "<ipython-input-35-0ed255af70d3>", line 1
        def f(x,y=2,z):
             ^
    SyntaxError: non-default argument follows default argument
    

    注意:預設值參數為運算式

    在函式定義時將成為預設值的運算式只會被執行一次
    得到物件後由函式物件記錄著,之後每次呼叫,只會得到之前的結果

    In [36]:
    a=3
    def test(x,y=a):
        print(x,y)
    test(1)
    a = 999
    test(1)
    
    1 3
    1 3
    
    In [37]:
    def test(x,y=[]):
        y.append(x)
        print(y)
    test(1)
    test(2)
    test(9,[5,7])
    
    [1]
    [1, 2]
    [5, 7, 9]
    

    改善寫法

    In [38]:
    def test(x,y=None):
        if y == None:
            y = []
        y.append(x)
        print(y)
    test(1)
    test(2)
    test(9,[5,7])
    
    [1]
    [2]
    [5, 7, 9]
    

    形式參數 *,**

    在函式「定義」時使用,可以收集額外參數

    *:用tuple處理額外的位置參數

    • 後方如果還有參數,必須以關鍵字參數指定
    In [39]:
    def f(x,y,*arg):
        print(x,y,arg)
    f(1,2,3,4,5)
    f(1,2)
    
    1 2 (3, 4, 5)
    1 2 ()
    
    In [40]:
    def f(x,y,*arg,a):
        print(x,y,arg,a)
    f(1,2,3,4,5,a=6)
    
    1 2 (3, 4, 5) 6
    
    In [41]:
    def f(x,y=1,*arg,a=2):
        print(x,y,arg,a)
    f(1,2,3,4,5)
    
    1 2 (3, 4, 5) 2
    

    **:用dict處理額外的關鍵字參數

    • 後方不能有位置參數、關鍵字參數
    In [42]:
    def f(x,y,**arg):
        print(x,y,arg)
    f(1,2,a=3,b=4,c=5)
    
    1 2 {'a': 3, 'c': 5, 'b': 4}
    

    綜合版

    In [43]:
    def f(x,y,*arg1,**arg2):
        print(x,y,arg1,arg2)
    f(1,2,3,4,c=5)
    
    1 2 (3, 4) {'c': 5}
    
    In [44]:
    def f(x,y,*arg1,a,b,**arg2):
        print(x,y,a,b,arg1,arg2)
    f(1,2,3,4,a=5,b=6,c=7)
    
    1 2 5 6 (3, 4) {'c': 7}
    

    實際參數*,**

    在函式「呼叫」時使用,可以解開參數

    *:傳入可迭代物件

    • 例如str、range、list
    • 後方只能接關鍵字參數
    In [45]:
    def f(a,b,c,d,e):
        print(a,b,c,d,e)
    f(5,6,*range(3))
    
    5 6 0 1 2
    
    In [46]:
    f(*range(3),d=5,e=6)
    
    0 1 2 5 6
    

    **:傳入映射物件

    • 例如字典
    • key中不能有與位置參數的名稱重複
    In [47]:
    def f(a,b,c,d,e):
        print(a,b,c,d,e)
    d = {"c":1,"d":2,"e":3}
    f(5,6,**d)
    
    5 6 1 2 3
    
    In [48]:
    d = {"a":5,"c":1,"d":2,"e":3}
    f(5,6,**d)
    
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-48-004c09ae30e9> in <module>()
          1 d = {"a":5,"c":1,"d":2,"e":3}
    ----> 2 f(5,6,**d)
    
    TypeError: f() got multiple values for argument 'a'

    補充知識-插入排序法

    不論你是在打牌,或是整理圖書館架上的書、衣櫃裡的衣服,
    都很常使用插入排序法 Insertion Sort。

    • 取出最後端的元素,從已排好的序列由後往前找到適當的位置插入(不斷交換)。
    In [49]:
    def insert_sort(l):
        n=len(l)
        if n==1: 
            return l
        for i in range(1,n):
            for j in range(i,0,-1): # 已排好的序列
                if l[j]<l[j-1]: 
                    l[j],l[j-1] = l[j-1],l[j]
        return l
    print(insert_sort([6,7,8,1,2,3,5,4]))
    
    [1, 2, 3, 4, 5, 6, 7, 8]
    

    變形

    我們在生活上其實比較像是這樣

    • 取出最後端的元素,從已排好的序列由前往後找到適當的位置插入。
    In [50]:
    def insert_sort(l):
        n=len(l)
        if n==1: 
            return l
        for i in range(1,n):
            for j in range(0,i): # 已排好的序列
                if l[i]<l[j]:    # 只要比較小就插入,因為越往後越大
                    value = l[i]
                    l.pop(i)
                    l.insert(j,value)
        return l
    print(insert_sort([6,7,8,1,2,3,5,4]))      
    
    [1, 2, 3, 4, 5, 6, 7, 8]
    

    其他的排序法

    學習資源對應