# Python 基礎語法
###### tags: `Python/SQL商業資料分析` , `Python`
[TOC]
# Python 基礎 I
## 註解
```text
# 單行註解
"""
多行註解
"""
```
## 變數(variable)
```text
/* name 變數指到 'Marry' 字串物件 */
name = 'Marry'
print(id(name))
# name 變數改指到 'Jay' 字串物件
name = 'Jay'
print(id(name))
```
明明是同一個變數卻印出兩個不同的記憶體位址,這是因為變數指到不同的物件而非真正儲存該字串值。
## 變數命名
Python 規定變數命名規則需要為英文字母開頭或是底線開頭(底線通常為私有變數命名使用)
- 開頭首字之後可以是字母、數字或是底線
- 大小寫是屬於不同變數
- 通常變數會用小寫命名,不同單字使用底線連接(ex. chance_of_rainfall)
- 變數中不能使用 . 和 ! 等符號
```text
# 錯誤使用變數方式
321name
# 正確使用變數方式
_name_321
name
```
## 基本資料型別
- **int 整數**:指沒有小數的數字
- **float 浮點數**:有小數的數字
- **bool 布林**:代表真假值(True/False),進行條件判斷使用
- **str 字串**:由一個個字元(character)組成的字串
- **NoneType 型別**:None 代表沒有值的物件
我們可以使用 type() 這個 Python 內建函式確認變數型別:
```text
# 使用內建函式確認變數型別
name = 'Jack'
# 變數型別 str
type(name)
```
## 型別轉換
可以使用下面函式進行型別轉換:
- int():轉成整數
- float():轉成浮點數
- str():轉成字串
```text
print(int(1.76))
print(float(3))
print(str(9))
```
程式執行結果:
```text
1
3.0
'9'
```
## 輸入輸出
在 Python 中使用 print() 進行輸出到終端機螢幕上,而 input() 則會讓使用者可以輸入內容接收並處理(資料型別為字串)。
```text
age = input('請輸入年齡')
print(age, type(age))
age = int(input('請輸入年齡'))
print(age, type(age))
```
## 運算式、運算子與運算元
在程式語言中敘述(statement)和運算式(expression)兩者差異在於敘述沒有將結果給定給一個變數值:
```text
# 敘述
1 + 2
# 運算式
sum = 1 + 2
```
### 運算子與運算元
- 運算子:符號
- 運算元:運算的值
### 賦值運算子
在 Python 一個等號是 `=` 給定變數的意思,而 `==` 才是比較是否相等。
```text
name = 'Joe'
```
### 計算運算子
`+` ---> 加法 ---> x + y
`-` ---> 減法 ---> x - y
`*` ---> 乘法 ---> x * y
`/` ---> 除法 ---> x / y
`%` ---> 取餘數 ---> x % y (例如:7 % 3 為 1)
### 比較運算子
`==` 等於 x == y
`<` 小於 x < y
`>` 大於 x > y
`<=` 小於等於 x <= y
`>=` 大於等於 x >= y
`!=` 不等於 x != y
> 在 Python 中有 boolean 布林值資料型別,使用 True(注意首字大寫)、False 來表示。
### 邏輯運算子
and-----且(皆為是才是)-------------x and y
or------或(其中一個為是則為是)----x or y
not-----否(邏輯相反)----------------not x
```text
age = 21;
grade = 60;
# 範圍等於 21-99
print(age > 20 and age < 100);
# 範圍等同於 >= 60
print(grade > 60 or grade == 60);
response = True
# false
print(not response)
```
## 字串物件
在 Python 中,字串和 C 語言一樣,索引是從 0 開始。
Python 字串物件宣告方式:
```text
# Python 中可以使用單引號或雙引號包裹字串
name = 'Jack'
```
### 跳脫字元 \
Python 中可以使用單引號或雙引號包裹字串,若在字串中需要顯示 '' 或 "" 等特殊符號就需要使用跳脫字元(escape)。
正確:\' 讓 ' 視為字串一部分,印出 Jack's 字串
```text
text = 'Jack\'s'
print(text)
// 執行結果:
Jack's
```
### 字串切片 slice
字串切片 slice:可以設定範圍取出子字串。
下圖我們可以知道字串 index 從左至右從 0 開始,反向則由 -1 開始。
![](https://i.imgur.com/VWPD85S.png)
取出前 3 個字元:
```text
text = 'hello'
# 注意 [:3] 代表從 0 到 2(不含結束的 3),所以是 0, 1, 2:hel
print(text[:3])
```
### 字串物件長度 len
可以使用 len(字串) ,得知字串長度為多少:
```text
text = 'hello world'
# 注意空白也算一個字元長度
print(len(text))
```
### 是否有在字串 in
可以使用 in 這個關鍵字,得知子字串是否有在字串中:
```text
text = 'hello world'
print('worl' in text)
```
執行結果:
```text
True
```
### 替代子字串 replace
想要替換子字串的內容的話可以使用 `replace(原子字串, 希望替換子字串)` 字串方法。
```text
text = 'hello world'
replaced_text = text.replace('hello', 'hi')
# 原來字串不變
print(text)
# hi, world
print(replaced_text)
```
### 補充字串插值(Formatted String Literal)
Python 3.6 新增了格式字串字面值(Formatted String Literal)此一作法可以把 Python 運算式嵌入在字串常數中。讓我們可以減少使用 + 來串連字串和變數,讓程式碼更好閱讀。
```text
text = 'world'
sentence = f'Hello, {text}'
print(sentence)
```
可以在裡面嵌入任何 Python 的**運算式**,舉例來說,我們想要呈現整數相加:
```text
x = 7
y = 41
result = f'x + y = {x + y}'
print(result)
# x + y = 48
```
字串格式化用於將變數整合到字串中,若是沒有使用則需要使用 + 號來串連字串和變數**(通常效能較差)**:
```text
age_1 = 10
age_2 = 7
text_1 = 'I am ' + str(age_1 + age_2) + ' year old'
print(text_1)
text_2 = f'I am {age_1 + age_2} year old'
print(text_2)
```
## 可變物件和不可變物件的差異
- 可變物件(mutable):可以更改內容的物件
- 不可變物件(immutable):不可以更改內容的物件
```text
language_1 = 'Python'
language_2 = language_1
language_2 = 'C'
# language_1 沒有影響,還是 Python
print(language_1)
# language_2 指到新的字串物件 C
print(language_2)
# 兩者指到的記憶體位置不同
print(id(language_1))
print(id(language_2))
```
> 若 'Python' 字串物件一直沒被使用,則經過一段時間就會被系統回收,Python 直譯器會刪除該物件。這動作就叫記憶體回收(garbage collection)
關於 Python Garbage Collection 機制使用 Reference Counting 的方式,當物件被使用的計數為 0 時,回收記憶體。
可變物件 list 更改內容後即更改:
- Python 常用的不可變物件有:整數、浮點數、布林、字串和 tuple(元組)等
- Python 常用的可變物件有:list(串列)、dict(字典)、set(集合)
## 容器物件概論
單純使用字串就會需要複製多次,有點不切實際,這就是容器物件可以派上用場的地方:
```text
student_1 = 89
student_2 = 61
student_3 = 56
# students list 可以儲存多筆資料
students = [89, 61, 56]
```
## tuple 容器物件基本操作
tuple(元組)為可以存放任何資料型別的一個容器物件,**注意 tuple 在 Python 是不可變的物件**。若是更新內部元素值會發生 `TypeError: 'tuple' object does not support item assignment `錯誤。
### tuple 的基本操作
#### 創建 tuple :
```text
# 使用 () 將元素包裹
age_1 = (1, 23, 41, 73)
# 若僅有一個元素則最後要加上 , 讓程式知道是 tuple 而非整數 (18)
age_2 = (18, )
```
#### tuple 取值:
```text
# 索引取值
print(age_1[0])
# slice 取值
print(age_1[:2])
```
#### 合併 tuple :
```text
age_1 = (1, 23, 41, 73)
age_2 = (18, )
# 相加
age_3 = age_1 + age_2
```
## list 容器物件基本操作
list(列表)可以儲存多個元素,但比起陣列多了許多彈性和功能(元素可以是不同的資料型別)。作
### 創建 list:
```text
names = ['Jack', 'Joe', 'Marry']
```
### 新增元素:
```text
names = []
# ['Amy']
names.append('Amy')
```
### 取值:
```text
names = ['Jack', 'Joe', 'Marry']
# Jack
print(names[0])
# ['Jack', 'Joe'] 不含結尾 2
print(names[0:2])
```
## set 容器物件基本操作
set(集合)類似於數學的集合概念和邏輯。最大的特色是**元素內容不重複**,可以用來判斷字串、整數等不重複個數。
> 注意 set 和 list 不一樣的是 set 不是循序,所以在操作時順序有可能是隨機
### 創建 set:
```text
languages = {'Java', 'C', 'Python'}
```
### 新增 set 元素:
```text
# 若為空 set 宣告需使用 set(),不能使用 {}
languages = set()
languages.add('Python')
languages.add('Java')
languages.add('Python')
languages.add('C')
# 去除重複值
# 注意 set 和 list 不一樣,不是循序序列,所以順序可能會有所不同
# {'Python', 'C', 'Java'}
print(languages)
```
### 取值(set 無法直接使用 index 取值):
```text
languages = {'Java', 'C', 'Python'}
# 使用 pop 方法從 set 中取出隨機的元素並移除
print(languages.pop())
```
### 轉為 list 進行 index 取值:
```text
# 轉為 list 進行取值
print(list(languages)[0])
```
## dict 容器基本物件
dict(字典)是一個 Python 常用容器物件,可以透過 key/value 鍵值方式來儲存資料,方便存取。
```text
dict_1 = {
'key_1': 'value_1',
'key_2': 'value_2',
'key_3': ['list_value_1', 'list_value_2']
}
```
### 創建 dict :
```text
# 宣告一個空 dict
dict_1 = {}
# 給定 key/value 值
dict_1['key_1'] = 'value_1'
print(dict_1)
# 直接初始化
dict_2 = {
'key_1': 'value_1'
}
```
### 運用鍵值取值:
```text
dict_2 = {
'key_1': 'value_1'
}
# value_1
print(dict_2['key_1'])
```
### 更新值:
```text
dict_2 = {
'key_1': 'value_1'
}
dict_2['key_1'] = 'value_2'
# value_2
print(dict_2['key_1'])
```
# Python 基礎 II
## if else 條件判斷
透過 `if`、`if else` 和 `if elif else` 進行條件判斷,根據條件執行不同的程式區塊(在 Python 以縮排區分程式區塊)。
if 語法:
```text
if 條件:
程式區塊
```
if else 語法:
```text
if 條件:
程式區塊
else:
程式區塊
```
if elif else 語法(如果符合條件 1 則,如果符合條件 2 則,否則):
```text
if 條件 1:
程式區塊
elif 條件 2:
程式區塊
else:
程式區塊
```
範例程式:
```text
age = 20
if age >= 20:
print('恭喜可以投票!')
else:
print('sorry, 還不能投票喔!')
```
## for 迴圈基礎
```text
result = 0
# range 可以產生一個可迭代的物件 range(起始, 結束, [間隔]) 。注意不含結束,舉例來說 range(1, 11) 從 1 開始不含 11。間隔為每個數字的間隔。若為 range(1, 11, 2) 則為 1, 3, 5, 7, 9,若無指定則預設為 1 ,亦可為負數
for number in range(1, 11):
print(number)
result += number
print('result:', result)
```
執行結果:
```text
1
2
3
4
5
6
7
8
9
10
result: 55
```
## for 迴圈取出 list 元素
```text
# 初始化一個 list 串列
languages = ['C', 'Python', 'Java']
# 從 languages 取出的元素 item 可以自行定義變數名稱
for item in languages:
print(item)
```
執行結果:
```text
C
Python
Java
```
## for 迴圈取出 dict 元素
```text
# 宣告一個產品 dict 存產品名稱和售價
products = {
'ASUS手機': 3490,
'iPhone20': 50000,
'三星手機': 12000,
'小麥手機': 2000
}
# 使用 for 迴圈會取出 dict key 值,key 也可以改成其他變數名稱
for key in products:
print(key)
```
執行結果:
```text
ASUS手機
iPhone20
三星手機
小麥手機
```
## while 迴圈
```text
counter = 1
result = 0
# 當 counter 大於 10 時跳出迴圈
while counter <= 10:
# 累加數字
result += counter
# 等同於 counter = counter + 1
counter += 1
print(result)
```
## 無窮迴圈
無窮迴圈代表沒有更新邏輯判斷條件,沒辦法讓程式最終有機會跳出迴圈。無窮迴圈有可能會造成電腦資源耗盡,程式當機。
```text
# True 代表程式會不斷進入迴圈無法跳出
while True:
print('無窮無盡不斷執行')
```
## break 使用方式
Python 具有 `break` 和 `continue` 兩個負責跳出迴圈的關鍵字功能。同樣地,`break` 代表的是跳出整個迴圈,而 `continue` 則是跳過這次迴圈後執行下一次迴圈,可以搭配迴圈使用,但要注意 while 更新條件,避免變成無窮迴圈。
```text
result = 0
# range 可以產生一個可迭代的物件 range(起始, 結束, [間隔]) 。注意不含結束,舉例來說 range(1, 11) 從 1 開始不含 11。間隔為每個數字的間隔。若為 range(1, 11, 2) 則為 1, 3, 5, 7, 9,若無指定則預設為 1 ,亦可為負數
for number in range(1, 11):
# 遇到 5 跳出迴圈
if number == 5:
break
print(number)
result += number
print('result:', result)
```
## continue 使用方式
```text
result = 0
# range 可以產生一個可迭代的物件 range(起始, 結束, [間隔]) 。注意不含結束,舉例來說 range(1, 11) 從 1 開始不含 11。間隔為每個數字的間隔。若為 range(1, 11, 2) 則為 1, 3, 5, 7, 9,若無指定則預設為 1 ,亦可為負數
for number in range(1, 11):
# 遇到 5 跳過這次迴圈直接執行下一次
if number == 5:
continue
print(number)
result += number
print('result:', result)
```
執行結果:
```text
1
2
3
4
6
7
8
9
10
result: 50
```
## 函式基礎
### 自定義函式
在 Python 定義函式使用 `def` 關鍵字:
```text
# 定義函式名稱和傳入參數
def add_number(num1, num2):
# 程式執行區塊
result = num1 + num2
# 回傳值
return result
```
呼叫函式:
```text
outcome = add_number(3, 7)
# 印出相加結果:10
print(outcome)
```
若是函式執行區塊還未定義只有定義名稱和參數時可以使用使用 pass 關鍵字當作佔位符號:
```text
def add_number(num1, num2):
pass
```
函式回傳值上可以使用單一值:
```text
def get_add_number(x, y):
return x + y
result = get_add_number(1, 3)
```
函式回傳值上可以使用 tuple 容器物件讓函式不只回傳一個值:
```text
def get_the_first_and_last_element(user_list):
user_list = user_list.copy()
user_list[0] = 'steven'
return (user_list[0], user_list[len(user_list) - 1])
user_list = ['jack', 'tom', 'toby', 'marry', 'amy']
(first_element, last_element) = get_the_first_and_last_element(user_list)
# steven, amy
# ['jack', 'tom', 'toby', 'marry', 'amy']
print(first_element, last_element)
print(user_list)
```
## 全域變數與區域變數
變數的生存域(scope)和可視範圍是一個蠻重要的觀念。意思是指在函式內宣告的區域變數(local variable)只能在函式內被使用,若是在函式外宣告的全域變數(global variable)則可以跨函式被存取使用。
```text
global_var = 3
def my_func():
# 宣告 global_var 為外面的全域變數
global global_var
# 宣告函式內的區域變數
local_var = 12
global_var += 1
return local_var, global_var
# 回傳 (12, 4)
my_func()
# 印出 4
print(global_var)
# 無法存取區域變數
print(local_var)
```
執行結果:
```text
4
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'local_var' is not defined
```
一般在函式內部使用的變數我們稱之為區域變數,其生命週期僅限於函式內,在函式外無法使用。若是在函式外部定義的變數,整個程式都可以使用的則稱為全域變數,一般若要在函式中使用會在變數名稱前面加上 `global` 關鍵字代表全域變數。
> 由於全域變數很容易被不同地方的程式使用到所以常常會產生不可預期的錯誤,一般會建議若能使用區域變數或是透過參數將變數傳入函式中盡量優先使用。
- 一、函式內區域變數修改不影響全域變數即便變數命名相同:
```text
number_var = 10
def my_global_var_function():
number_var = 7
# 印出區域變數 7
print(number_var)
my_global_var_function()
# 函式內修改的是區域變數,執行完後 number_var 印出仍是 10
print(number_var)
```
- 二、若於函式中未宣告變數直接存取全域變數相同名稱的變數則會存取到全域變數:
```text
number_var = 10
def my_global_var_function():
# 由於沒有宣告任何變數,程式語言設定會往外找同樣名稱的全域變數,故印出全域變數 10
print(number_var)
my_global_var_function()
print(number_var)
```
- 三、若於函式中宣告變數為 global 關鍵字全域變數,則修改該變數指定的物件則會更動到全域變數:
```text
number_var = 10
# 10
print(number_var)
def my_global_var_function():
# 在函式內宣告 number_var 使用的是全域變數
global number_var
# 修改全域變數為 7
number_var = 7
print(number_var)
my_global_var_function()
# 7
print(number_var)
```
- 四、綜合範例:
```text
# 宣告在函式外面的全域變數
number_var = 10
def my_local_var_function():
number_var = 23
print(number_var)
# 印出區域變數 23
my_local_var_function()
# 印出全域變數 10,不受影響
print(number_var)
def my_global_var_function():
global number_var
# 印出全域變數 10
print(number_var)
# 修改全域變數
number_var = 7
# 7
print(number_var)
my_global_var_function()
# 全域變數被修改成 7
print(number_var)
```
## 常用內建函式
想了解 Python 是否已經有類似函式可以使用的話,可以[參考官方文件查詢](https://docs.python.org/zh-tw/3/library/functions.html) 或是於搜尋引擎搜尋。
- `dir(物件)`:得知該物件有哪些可以使用的物件方法
```text
name = 'John'
dir(name)
```
執行結果:
```text
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
```
- `range()`:可以產生可迭代物件,讓我們可使用迴圈來取出序列值。透過 list(range()) 可以將 range 物件轉成 list 容器物件。
```text
nums = range(1, 11)
print(list(nums))
```
執行結果:
```text
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
```
- ` max()`、`min()`:
```text
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(max(nums))
print(min(nums))
```
- `len()`:回傳物件的長度,例如字串長度、list 串列內容長度
```text
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(len(nums))
```
## 類別基礎
Python 的物件是從 class 類別所產生的,所以我們使用 type(物件) 時可以看到事實上,**物件都有其對應的資料型別的類別**,其定義了產生物件的屬性和方法(物件可以使用的函式)。
```text
name = 'Jack'
print(type(name))
num1 = 19
print(type(num1))
num2 = 2.34
print(type(num2))
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(type(nums))
name_dict = {
'Jack': 21
}
print(type(name_dict))
```
執行結果(可以發現每一個物件屬於一個資料型別類別):
```text
<class 'str'>
<class 'int'>
<class 'float'>
<class 'list'>
<class 'dict'>
```
## 自訂類別
除了內建的類別外,也可以自訂類別。讓程式更加模組化和更好的重複使用和維護(尤其是未來如果你有開發複雜的應用或是遊戲時就會運用到類似的觀念和方法:物件導向程式設計)。可以想成 class 類別是物件的製造機,創建物件的過程又稱實例化(透過 class 產生 instance)。
```text
class Product:
# __init__ 為定義的初始化物件屬性值的設定方式
# self 代表該物件, 傳入參數會給該物件屬性 self.屬性
def __init__(self, name, price):
self.name = name
self.price = price
def get_discount_price(self, discount_rate):
"""
參數傳入折扣率,回傳該商品回傳
"""
return self.price * discount_rate
```
物件實例化(透過類別產生物件,可以依照需求產生數量):
```text
product_1 = Product('iPhone 手機', 20000)
product_2 = Product('ASUS 手機', 3200)
# 印出產品1名稱
print(product_1.name)
# 印出產品2名稱
print(product_1.price)
# 打九折價格
print(product_1.get_discount_price(0.9))
# 印出產品2名稱
print(product_2.name)
# 印出產品2價格
print(product_2.price)
# 打九折價格
print(product_2.get_discount_price(0.9))
```
執行結果:
```text
iPhone 手機
20000
18000.0
ASUS 手機
3200
2880.0
```
## Python 套件/模組基礎
模組就是一個 Python 程式檔案可以讓其他程式引用,而套件則是透過一個資料夾,裡面放置多個模組檔案來讓其他程式引用,適合比較複雜的模組程式管理。
### 自定義模組
在 `main.py` 檔案同一層新增一個模組檔案 `my_module.py`,讓 `main.py` 主程式可以讀取使用:
my_module.py:
```text
def add_num(num1, num2):
return num1 + num2
```
main.py:
```text
# 引用模組
from my_module import add_num
# 呼叫程式
result = add_num(1, 7)
print(result)
# 8
```
### 自定義套件
在 `main.py` 檔案同一層新增一個套件資料夾` my_package`,和 `my_module1.py` 及` my_module2.py`。讓 `main.py` 主程式可以讀取使用:
my_module1.py:
```text
def module1_func():
return print('hello module1_func')
```
my_module2.py:
```text
def module2_func():
return print('hello module2_func')
```
main.py:
```text
from my_package.my_module1 import module1_func
from my_package.my_module2 import module2_func
module1_func()
module2_func()
# hello module1_func
# hello module2_func
```
## 使用內建和第三方套件/模組
使用其他程式設計師開發的第三方模組,可以在電腦上安裝即可使用。若你的本機端電腦已經有安裝好 Python 開發環境,在 `Windows CMD` 或是 `MacOS/Linux` 終端機作業系統執行以下指令就可以安裝該第三方套件。
```text
# pip install 套件名稱
pip install numpy
```
```text
# 引入 numpy 別名使用 np 之後就可以使用 np 取代 numpy 可以少打點字
import numpy as np
# 產生五個元素為 0 的 numpy 陣列 [0, 0, 0, 0, 0]
np.zeros(5)
# array([0., 0., 0., 0., 0.])
```
# 延伸閱讀
- [SQL 資料庫](https://hackmd.io/fQbgqRSYTDi2Ouis0fE3rg?both)
- [Python 進階語法使用](https://hackmd.io/QYVYsxE8QyWNsnIhukt-2Q)
- [Python 網頁爬蟲](https://hackmd.io/JOc4g8AjSZiokS6vZckFSw?view)
- [Python 資料分析](https://hackmd.io/C5I9OzXzQCe6wJuZhwRV3Q)
- [Python 資料科學與探索式資料分析](https://hackmd.io/qSceMWZWQcWsMIA9QiO1Nw?view)
- [營收與使用者行為資料分析專案](https://hackmd.io/i6kRZN8JQsq57uDrKeKoLQ)
- [Python專案實作 資料分析與爬蟲](https://hackmd.io/oh18KsFvSxe5Eh3ECHDJOA?view)