iT邦幫忙

2022 iThome 鐵人賽

DAY 2
0

前言

我們前一篇介紹了安裝、基本語法以及資料型態,這一天我們就是銜接上一天來做進階介紹,
理論上在經過這兩天之後你就可以出去大喊我會 python 啦 (會hello word 也是會啊),
所以把今天好好吸收之後,我們就可以直接來 AI 的部分惹。

進階語法

循環語法

range() 方法

在開始介紹Python迴圈之前,
先來說明一個在執行迴圈時常用的range()方法,
主要用來幫我們產生數列,語法如下:
range(起始值,結束值,遞增(減)值)

使用說明:

  • range(10):起始值預設從0開始,所以會產生0到9的整數序列。
  • range(20,30):起始值從20開始,所以會產生20到29的整數序列。
  • range(20,30,3):起始值從20開始,遞增值為3,所以會產生20,23,26,29的整數序列。

For-Loops 循環

可以針對Iterable(可疊代的)物件來進行讀取,
像是Python內建幾個常用的Iterable物件,
例如:String(字串)、List(串列)、Tuples(元組)、Dictionary(字典)等。
Python for-loop的語法如下:

for item in iterable:
    statement

枚舉 enumerate()

在語法中,
in 的後方就是 for-loop 要讀取的目標物,
這個目標物的為Iterable (可疊代的)物件,一次讀取一個元素,
然後用 item (自訂變數名稱)來接收每次讀取到的元素,執行區塊中的運算。
注意for-loop的結尾需加上冒號 ( : ) 及區塊中的運算式要有相同的縮排,

範例如下:

在範例中,For-loop的讀取目標物為一個字串,每一次讀取一個字母,
並且用word變數來接收,執行print()輸出方法。

While 循環

while 語法用於循環執行程序,意思是在某特定條件下,
循環執行該項程序,以處理需要重複處理的相同任務。

概念如下:
while 判斷條件():
    執行語句()

可以理解成,當判斷條件為真才執行語句 ,

這邊的話,提供一個動圖方便理解概念,

接下來就是範例介紹:

當我們輸入下述程式碼,
a = 1
while a <= 10:
    print(a)
    a = a+2
    
實際運行就會得到結果如下

1
3
5
7
9

解釋上,首先宣告 a = 1 , 然後當 a 小於等於 10 時 ,輸出 a 同時將 a+2 ,
這樣整串程式碼會重複執行至 a 不再小於等於 10 。
在這樣的過程中,我們可以設想到一個狀況,
如果判斷永遠等於 True ,那會發生什麼狀況呢?

首先我們輸入該程式碼,
a = 1
while a == 1:
    print(a)

會得到結果是無數的 1 ,這時就需要 control + c 來強制中斷該程序。

補充

while 與 For-Loop 的部分還有判斷用法,
以 while 來舉例:
a = 1
b = 2
while a == 1:
    print(a)
else:
    print(b)
    
這時就會回傳 a ,假設 a 不是 1 ,才會回傳 b 。

判斷語法

Python 語言中提供了 if 、 else 、 elif 這三種語法來協助各種條件判斷和流程控制。
Python 一行一行執行的,所以當我們想要所寫的程式在某些條件下跳過某幾行敘述,就可以使用條件判斷。
也就是說,如果要讓程式可以自動檢查所處理資料的內容,
而且根據資料內容來決定是否執行某一個敘述或指令,那就需要用到條件判斷式來控制流程。

  • if 敘述
程式在進行的過程,需要根據某個條件來決定是否執行接下來的動作時,
可以透過:

if abc == True:
    print("hello world")
    
來進行條件判斷,如同字面上的意思,當宣告的變數等於"真"時,
就會輸出自定義的訊息,如果當宣告的變數或條件不相等時,
便會跳過這個判斷直接往下執行。
  • if-else 敘述
當今天我們要追求非真及否的狀況下,
就會運用到:

if abc == True:
    print("hello world")
else abc == False:
    print("This is False")

來進行條件判斷,如同字面上的意思,當宣告的變數等於"真"時,
便會輸出 hello world 如果等於"否",就會輸出 This is False,
但有些情況下我們只想要宣告的變數等於特定值,如果等於其他值便給予統一的回覆,
則可以這樣運用:

if abc == True:
    print("hello world")
else:
    print("This is False")

這樣只有當宣告的變數等於"真"時,才會給予 hello world 的回覆,如果變數是其它任何數值,
都會給予 This is False 。
  • if - elif - else敘述
有的時候需要判斷的可能狀況有很多種時,便會需要用到這個狀況,
例如:

if abc == 1:
    print("number is 1")
elif abc == 2:
    print("number is 2")
else:
    print("number")

如同上述字面上的意思,當數值為特定的時後會給予定義的數值,
如果都不是就單純輸出 number 字串。

  • 巢狀 if 敘述
當我們要在判斷條件中安排更進一步的判斷條件時,就需要用到巢狀結構了。
所謂的巢狀 if 敘述是指在 if-else 敘述當中,還有另一組 if-else 敘述,
例如:

id = "Andy"
age = 20
if age < 10:
    print("not Andy")
elif id == "Andy"  and age <= 20:
    if age == 20:
        print("is Andy")
    elif age == 18:
        print("Andy age not 18")
    elif age == 16:
        print("Andy age not 16")
    elif age == 14:
        print("Andy age not 14")
else:
    print("not Andy QQ")

上述判斷當 id 等於 Andy 且 age 等於 20 時,才是 Andy 。

例外陳述

例外處理 (exception handling) 是利用 try 、 except 、 finally 及 else 來進行判斷,

當今天我們已經得知執行 1 除以 0 的情況,會得到 ZeroDivisionError: division by zero
這個錯誤回報,所以我們假定一個方法如下:

try:
    print("程式運行至點位0")
    result = 1 / 0
    print("程式運行至點位1")
    
except Exception as e:
    print("錯誤信息",e)
    print("程式運行至點位2")

運行完後會我們可以得到,

從上述程式碼運行結果可以得知,try expect 語法無論如何都會先從 try 語法一句一句執行,
假設遇到 error 才會中斷,從 expect 再繼續執行,
上段程式碼我們先輸出 " 程式運行至點位0 " ,然後當它執行完 result = 1 / 0 的語句時,
便會中斷沒有繼續往下執行輸出 "程式運行至點位1" 。
因為 result = 1 / 0 單獨運行時我們已經得知它是個錯誤的語法,會回傳 ZeroDivisionError ,
所以我們 except Exception 是 error 的時候,我們輸出錯誤訊息 e ,且繼續輸出運行點位 2。

所謂例外 (exception) 是指已知有可能發生的錯誤 (error) ,像是開啟檔案,檔案卻不存在,
或除數為 0 等等的情況。

透過這樣的簡單運用可以得知,

  • try: 所有可能發生例外的程式碼都要放在 try 來嘗試是否有錯誤
  • except: 後空一格接例外類別 (class) ,底下程式區塊做相對應的例外處理
  • else: 假設沒有例外發生的處理
  • finally 是例外處理結束後,無論如何都會執行的部分 (可有可無)

在做個簡單的程序判斷來更充分了解上面四段的意思,

透過輸出可以得知由於 try 裡面的語法有錯誤所以程式碼往 except 繼續執行,
且由於有錯誤不會執行 else ,最後執行 "無論有無錯誤" 都會執行的 finally 。

列表推導式

列表推導式 (list comprehension) , 可以用英 文List Creation from For Loop 來理解,
用以創建具有篩選能力的迴圈,其結果存為 列表 (list)。
說起來很像繞口令的廢話,但實際上能提供的效用卻非常大,
例如一般情況下新增資料進入一個預設好的列表:

list1=[]

for i in range(10):
  list1.append(i)

print(list1)

上段程式碼會得到結果為 : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

但我們可以透過一句程式碼:

[i for i in range(10)]

便得到一樣的結果 : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

這個用法便為列表推導式,列表推導式並不是什麼特別的技術,它只是一種創建列表的簡潔方法,目的是為了讓大家寫程序時更方便更快捷,寫出更簡潔的程式碼。

同樣,如同一般程序,邏輯程序的結果存成 List ,所以我們可以透過語法的編寫來取得我們想要的 List,

[ i*i for i in range(10)]

將 for i in range(10) 的 i*i
來得到 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

透過一個練習來更加明白列表推導式的用法:

僅修正以下程式第4行,來使輸出結果為 [2, 4, 6, 20, 20]。

原先的程式碼我們可以得知, for i in bx 且 當 i % 2 > 0,
我們將 list 透過枚舉 ( enumerate ) 的方式,來得到它的 key 跟 value ,
list 的第 0 筆資料是 1 ,所以 0 是 key、1 是 value,以此類推,
觀察可得知列表中數值 2 , 4 , 6 位於 key , 1 , 3 , 5 的位址,
所以我們將 i (也就是 key ) %2 只要大於 0 便是 key 為奇數的位址,
再將 bx[i] 存進 list 便能得到我們想要的 [2, 4, 6, 20, 20]
如果覺得上述很繞口很難理解的話,用另外一種方式呈現來看:

原本的列表 bx 應該是 [1,2,3,4,5,6,20,20,20,20] ,

這些數值分表位於列表中的位址是:
[1,2,3,4,5,6,20,20,20,20]
 0 1 2 3 4 5 6  7  8  9

可以得知題目的 [2, 4, 6, 20, 20] 分別位於列表中的位址 1,3,5,7,9
只需要將 key 除以 2 餘數大於 0 的儲存即可。

函數定義

函數是預先建構好的可重復使用的,用來實現單一或相關聯功能的程式碼,能提高應用的模塊性,
和程式碼的重複利用率。Python 本身提供了許多內建函數,比如 print(),但也可以自己創建函數,這被叫做用戶自定義函數。

函式以 def 關鍵字定義,並以 return 將結果輸出。
一個函式只應該做好一件事情,太複雜拆開減少耦合,引數原則至多讓使用者輸入 2 個,其他引數都有預設值,引數為區域變數,計算結果透過 return 傳出。

好,上述應該講的非常文言,還是透過實際編寫來了解狀況:

首先我們定義一個函式叫做 add_number 且提供兩個變數 number1 , number2 供使用者自定義輸入,函式內進行邏輯運算,將 number1 + number2 的結果存為 Ans 且最後回傳。

如果函式結尾我們沒有以 return 敘述回傳,輸出將為空,意思就是函式處理的結果不會傳出來,
而函式內的 Ans 為區域變數,也就是在其他函式或函式外無法引用這個 Ans 。

在上圖的程式碼中可以看到,我們輸出函式 test 的結果為 1 ,但我們要單獨輸出 local 這個變數卻顯示尚未被定義,這就是因為我們只有將 local 設為函式 test 內的變數,

而在上圖的程式碼中我們可以看到,當 local 在函式外時無論函式內外都可以成功呼叫到這個變數,
那我們該如何將區域內的變數設為全域變數呢?

我們可以透過在宣告變數前先使用 global 這個方法將 local 預先設定為全域變數,
如此的話無論函式內外都可以成功呼叫。

函式的引述 *args 及 **kwargs

在函式中我們常常可以見到 *args 及 **kwargs ,那他們分別代表甚麼意思呢,
首先來看看範例:

在上圖程式碼中可以看到我們將函式的可引入變數設成 *args,這個數值可透過使用者自定義輸入,
透過 * 收集的引數會被放到一個元組 (tuple)中,所以我們可以使用 for 迴圈來對它進行迭代。
使用 *args 的好處是我們當今天不確定預先輸入至函式的變數有多少時,
我們可以透過這個方法來避免輸入至函式的變數比預先設定的多,導至報錯。

那這邊就會疑問啦,那 **kwargs 呢,再來看新的範例:

這邊我們將函式的引入變數設成 (name="阿柴",age="21"),它會自己把它拆開變成 dict 格式,
那是不是好像懂了什麼 ?
概括的說一個 * 號類似於將資料蒐集存成 turple ,兩個 * 號則是蒐集完並且存成 dict,
那這樣的運用便十分得宜了,我們可以這樣用,

這邊就可以得知,我們第一個引入變數 0 被 money 接走了,多餘的則是被 args 接走,
而當我們引入變數是類似宣告較為具名者時(Ex:abc=2),它則會被 kargs 接走。
唯一要注意的是,* 一定要在 ** 的前面,而呼叫函式時有名字的也一定要在沒名字的後面。
這種的寫法通常會在裝飾器時使用,讓裝飾器可以接受參數數量不同的函式。

函式裝飾器

函式裝飾器又叫做 Decorator 也稱為語法糖,因為 Python 的函式也是物件,
所以可以達成用函式做為一個引數引入另一個函式,而裝飾器提供了簡潔的方式處理函式的傳入。

我們將 Decorator 設定成一個函式的名稱,裡面再包含了一個實際上想要提供功能的函式,
也就是外面的函式實際上只給予名稱,內層的函式才是給予功能,
再將它用小老鼠 @ 加在你想要提供的函式上面,便能給予一個函式更多的效果。
那其實流程上來說能夠看見,實際上在進入 age 函式之前它就已經進去 Decorator 函式裡面了,
由於我們 Decorator 裡面輸出 "我是阿柴" 之後便呼叫了 age 這個函數引數,
所以會先輸出 "我的年紀是21歲喔" 才輸出 "謝謝大家",
由此可知裝飾器的可操控性非常高,可以透過自己對於想要達到的目的來自由定義,
語法糖是 python 非常好用的功能,因為它能為每個程式碼來提供特一功能的函式,
避免函式重複撰寫,造成空間的浪費以及程式碼的雜亂,也易於後續的維護或者程式碼更改。

匿名函式 lambda

首先我們在一般定義一個函式的時候,應該是長成這樣的,


def add(a,b=1):
    ans = a + b
    return ans

但 Python 提供我們另外一種方式去定義函式,也就是 lambda ,
那它法其實很獨特,我們要定義匿名函式的話,需要這樣,以上面的函式為例子,

test_lambda = lambda a ,b=1 : a+b

注意到,它其實跟一般的語法很相像,
不過它在等號的右方是直接宣告 lambda 然後給予兩個參數 a b ,
然後在 : 符號後面定義函式應有的運算邏輯,

但這不是 lambda 的單一用法而已,


test2_lambda = lambda x: "數字是1" if x = 1 else "數字不是1"

我們可以透過這樣的方式,來達到判斷式的效果。

所以可以理解,在撰寫 Lambda 函式時,包含了三個部分:

  • lambda 關鍵字
  • parameter list 參數清單
  • expression 運算式

參數清單也就是 Lambda 函式的傳入參數,可以有許多個,用逗號分隔。
而運算式則是針對傳入參數來進行運算,只能有一行運算式,不像一般函式可以有多行邏輯。

Lambda 函式支援 IIFE (immediately invoked function expression) 語法,意思是利用 function expression 的方式來建立函式並且立即執行它,語法如下:


(lambda parameter: expression)(argument)

那看到上面那串英文應該會感覺很疑惑,那是啥,
其實很簡單,我們這樣測試,


a = 1
print(lambda a:print(a))

<function <lambda> at 0x000001EFAE7EC0D8>

我們在運行上面 print(lambda a:print(a)) 會知道,
當Lambda函式經定義後,沒有進行呼叫的動作,
它會回傳一個 function object 且包含了記憶體位址,
如果我們想要印出值則可透過上面說的的IIFE語法進行呼叫,也就是:

(lambda a:print(a))(a)

就可以得到結果 a = 1 了。

這樣比較下來 lambda 與一般函式的差別在於,

  • Lambda函式不需要定義名稱,而一般函式 ( Function ) 需定義名稱。
  • Lambda函式只能有一行運算式,而一般函式 ( Function ) 可以有多行運算式。
  • Lambda在每一次運算完會自動回傳結果,而一般函式如果要回傳結果要加上 return 關鍵字。

補充一下,上面提到 IIFE 語法 全名為 Immediately Invoked Functions Expressions,

指的是可以立即執行的 Functions Expressions 函式表示式,中文多譯為立即(執行)函式。

讀寫檔案

在 python 應用中,讀取檔案或寫入檔案,是基礎中的基礎,也是不可或缺的技術,
因為我們有各種需求須將結果或過程寫入特定檔案以供使用者瀏覽,
那接下來就是介紹如何寫入各種檔案類型,

讀寫 txt 檔

使用 open("檔案","模式") 的開啟文件
常用的模式有以下幾種:

  • r : 讀取
  • w : 寫入 (若檔案存在則清空取代)
  • a : 在既有檔案最後面寫入
  • r+: 讀取資料並且可由開頭寫入(會取代掉開頭原文字)
  • w+: 同 w 但同時也可讀取
  • a+: 同 a 但同時也可讀取

打開之後當然是要讀取,所以我們在打開的時候通常會這樣撰寫,

filedata = open("檔案","模式")

接下來再以 .read 的方式回傳檔案內容

filedata.read()
  • .readline() - 讀取一行,最後面會自動加上一個 \n (跳行)
  • .readlines() - 傳回一個list,每一行文字最後面會加上一個 \n ,為一個 list 的資料項。

寫入的話也十分簡單,透過以下方式,

filedata.write(string)

便可直接寫入,不過在程式結尾都必須加上一個 .close() 來做為結束。

讀寫 json 檔

在讀寫 json 檔之前,有必要先了解一下何謂 json ?
根據維基百科的說明,json 的全名是(JavaScript Object Notation,JavaScript 物件表示法),
是一種資料格式,並且支援多種程式語言,它的資料格式有2種:

  1. 物件(object): 一般用大括號{}表示
  2. 陣列(array): 一般用中括號[]表示

物件以(key-value)的方式儲存,很像是python裡的字典,
例如說 {"Name": "阿柴", "age": 21} 便是一個物件的例子,

不過 json 的 key 值只能是文字, {10: "age"} 就是錯誤的例子,

!!! 注意json的字串是雙引號 !!!

如果在 python 中想要引用 json 模組,
需先寫上 import json 作為引入,

在模組中,我們要記兩個函數 dumps 與 loads ,

  • dumps: 將 python 的資料轉換成 json 格式
  • loads: 將 json 格式的資料轉換成 python 的資料

接下來來做幾個例題,

dumps 的部分
data = {'dog':'阿柴','age':21}
ans = json.dumps(data)
print(ans)

loads 的部分

data = '{"dog":"阿柴","age":21}' 
ans = json.loads(data)
print(ans)

不過 python 內的字典是無序的資料,
若在 dumps 函數內加入參數 sort_keys = True,
可以將轉成 json 的格式的物件資料依 key 值排序,

import json
data = {'蔡鷹文':15, '韓國瑜珈老師':20, '宋楚瑜清芳': 10, '阿柴': 60, '美國瑜': -3}
ans1 = json.dumps(data)
ans2 = json.dumps(data, sort_keys = True)
print("未排序:", ans1)
print("排序後:", ans2)

有同學會好奇,上面這樣中文有亂碼怎辦,
這時候就要加上 ensure_ascii = False ,也就是,

ans1 = json.dumps(data,ensure_ascii=False)

就可以成功輸出了,介紹完這兩個方式後,該來讀寫檔案了,

在 python 中用來讀寫檔的語法為 with...open,我們可以透過它來,

寫檔

import json

data = {'蔡鷹文':15, '韓國瑜珈老師':20, '宋楚瑜清芳': 10, '阿柴': 60, '美國瑜': -3}
file = 'data.json'
with open(file, 'w') as obj:
    json.dump(L, obj)

讀檔

import json
file = 'data.json'
with open(file, 'r') as obj:
    data = json.load(obj)

print(data)

注意啦 json 檔讀檔進來後就是 python 的字典型態,而非字串ㄛ。

今天結束之後,一樣送大家一首歌,林宥嘉 傻子 (我不是說他傻子,是他的歌名就是傻子)
https://www.youtube.com/watch?v=xi6wOaZ61-U&list=PLZ_d6NX2sE80hOIrDy5J6vaCS53vj0oo7&index=14&ab_channel=%E8%8F%AF%E7%A0%94%E5%9C%8B%E9%9A%9B


上一篇
Day1 AI 前言 Python 安裝與基本語法介紹 (一)
下一篇
Day3 深度學習基本名詞介紹
系列文
數位中介法沒有屏蔽的 AI 大數據大補帖30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言