# 語言基礎
###### tags: `python`
## 標準輸入與輸出
#### 標準輸出函式:`print()`
用來將資料輸出到終端(terminal)畫面上。
```
print('Hello Python')
```
會看到輸出:
```
Hello Python
```
可以輸出多筆資料,每筆資料使用逗號「,」隔開:
```
print('Hello Python', 2021)
print('Today', 'is', 'Sunday')
```
會看到輸出:
```
Hello Python 2021
Today is Sunday
```
> **注意**
>
> 使用單引號「'」或雙引號「"」包住的資料在Python中稱為「字串」,只要是文字都必須使用單引號或雙引號在左右兩邊包圍著,而且也必須這麼做,否則會出現錯誤,而數字則是沒有使用單引號或雙引號包圍的數字。
>
> 在Python中如果數字左右兩邊有使用班引號或雙引號包圍著,對Python來說,它會是一個字串,而不是數字。
#### 標準輸入函式:`input()`
用來從終端視窗上接收使用者的輸入,輸入的資料為字串型態。
```
a = input()
print(a)
```
1. 執行後畫面會停住,程式碼會暫停在`input()`該行等待使用者輸入,並按下「Enter」結束輸入的動作。
2. 使用者完成輸入後`input()`函式會傳回使用者的資料,此時需要使用指派運算子「=」,將資料存到變數(也就是電腦的記憶體)中,以避免程式碼往下執行時,資料消失,上面範例中`a`即為該變數名稱,透過`print()`函式,就可以將`a`的內容顯示到標準輸出。
可以在等待使用者輸入時,顯示提示文字:
```
a = input('請輸入您的姓名:')
print('您的姓名為:', a)
```
## 基本資料型態
### Python基本資料型態(或稱為內建型態)
有:
- 數值型態(Numeric type) - `int`, `float`, `bool`, `complex`
- 字串型態(String type)- `str`
- 容器型態(Container type) - `list`, `set`, `dict`, `tuple`
> Python 3後,整數型態一率為int,不再區分整數(int)與長整數(long),而且整數的長度沒有限制。
#### 數值型態
##### 整數型態 - int
Python 3後,整數型態一率為int,不再區分整數(int)與長整數(long),而且整數的長度沒有限制。
- 十進位整數:預設的整數位數,例如:9、89、135等等。
- 二進位整數:在數字前加上0b或0B,後面接上0~1的數字,例如:0b0001、0B10011100。
- 八進位整數:在數字前加上0o或0O,後面接上1~7的數字,例如:0o6、0O163。
- 十六進位整數:在數字前加上0x或0X,後面接上1~9以及A~F的數字,例如:0x1A、0X8C60。
從Python 3.6開始,數字可以加上底線,這對很長的整數閱讀上,會很有幫助。
例如:
```
1_000_000_000
0x12_34_56_78
0b1000_1101_0101
```
##### `print()`函示
為了可以輸出結果到終端機視窗,可以使用`print()`函示,如果有多個結果同時輸出,可以使用逗號`,`分隔。
例如:
```
print(234)
print('Hello!', 456) # 同時有多個結果要一次輸出
```
會輸出:
```
234
Hello! 456
```
如果想要輸出多的字串時,每個字串用逗號「`,`」做分隔,可以加上`sep=', '`,例如:
```
print('Hello!', '456', sep=', ')
```
會輸出:
```
Hello!, 456
```
`print()`函式預設輸出後一定會做換行,如果不想換行,可以指定`end=''`,例如:
```
print("Hello, ", end='')
print("World!")
```
會輸出:
```
Hello, World!
```
##### `type()`函式
無論是十進位、二進位、八進位、十六進位整數,都是int類別,如果想知道某種資料的型態,可以使用`type()`函式來取得,並搭配`print()函式`來顯示到終端機視窗上。
例如:
```
print(type(10))
print(type(0b1001))
print(type(0o123))
print(type(0x9C))
```
終端機視窗會看到這樣的結果:
```
<class 'int'>
<class 'int'>
<class 'int'>
<class 'int'>
```
##### 浮點數
浮點數是`float`型態,也就是帶有小數點得的數字。
例如:
```
3.14159
6.0
```
##### 複數
複數的形式為`a + bj`,型態為`complex`,複數可以直接做運算。
例如:
```
print(3 + 2j + 5 + 6j)
print(type(5j))
```
會輸出:
```
8 + 8j
<class 'complex'>
```
### 布林型態
布林型態的值只有`True`和`False`兩種,為`bool`型態,`True`代表的是「真」,而`False`代表的是「假」。
使用`type()`函式:
```
print(type(True))
print(type(False))
```
會輸出:
```
<class 'bool'>
<class 'bool'>
```
> 請注意`True`和`False`兩個單字的第一個字母都必須為大寫,不可為小寫。
### 字串型態
Python中要表示字串,可以使用一對單引號' '或一對雙引號" "來包圍要表示的文字。
例如:
```
'Hello, World!'
'+886-1234567'
```
單引號和雙引號的功用一樣,可以視情況做互換,例如字串本身包含有單引號時,就可以用雙引號來包圍字串。
例如:
```
"I'm a good student"
'Here is my "STUFF"!!'
```
字串裡定義了一些特殊的轉義文字,有特殊功用或意義,不會直接在畫面上輸出。
| 符號 | 說明 |
| ---------- | -------------------------------------------------- |
| `\\` | 反斜線`\` |
| `\'` | 單引號`'`' |
| `\"` | 雙引號`"` |
| \ooo | 以8進位字元碼顯示字元,最多三位數,例如:`\101' |
| \xhh | 以8位元16進位字元馬顯示指定字元,例如:\xE8 |
| \uhhhh | 以16位元16進位字元馬顯示指定字元,例如:\u32E8 |
| \Uhhhhhhhh | 以32位元16進位字元馬顯示指定字元,例如:\U000032E8 |
| \0 | 空字元,而不是空字串,等同於`\x00` |
| \n | 換行 |
| \r | 歸位(同一行) |
| \t | TAB |
```
'Hi,\nGood Morning'
"1\t2\t3"
```
如果字串內想要顯示像`\t`這樣的字串的話,必須寫成`\\t`或是可以在字串前面加上`r`代表直接使用原始字串而不需要轉義,例如:
```
print('\\t')
print(r'\t')
```
輸出為:
```
\t
\t
```
在使用單引`' '`號或雙引號`" "`表示字串時,程式碼不可以換行,如果字串內容必須換行,可以使用`\n`或三重引號,在三重引號內輸入的任何內容和格式都會被如實保留,例如:換行、縮排等等。
```
print('''This is Aaron's book,
May I talk to Amber.
Good morning~!```)
```
輸出為:
```
This is Aaron's book,
May I talk to Amber,
Good morning~!
```
> Python本身不支援多行註解,但是因為三重引號內可以被輸入任何內容,因此如果有暫時不想執行的程式碼都可以用三重引號來包圍,變相作為多行註解使用。
##### 練習
下面數字是什麼型態?
```
0xf
0.0
0o8
'4.4'
3.1e-1
999
0a
0b110
```
#### 字串格式化
Python有三種格式化字串的方式,用來讓字串可以根據不同的時間顯示不同的結果。
##### 第一種:%格式
像C風格一樣的格式化字串方式,例如:
```
print('Python version: %.2f' % (3.9))
print('I\'m %d years old, and you are %d.' % (18, 19))
print('Hello %s!' % ('Aaron'))
```
輸出:
```
Python version: 3.90
I'm 18 years old, and you are 19.
Hello Aaron!
```
> 舊方法,看看就好。
##### 第二種:str.format()
[PEP 3101](https://www.python.org/dev/peps/pep-3101/) 定義了透過呼叫 `str.format()`方法來格式化字串。
```
print('Python version: {}'.format(3.9))
print('I\'m {} years old, and you are {}.'.format(18, 19))
print('Hello {}!'.format('Aaron'))
```
> 舊方法,看看就好。
##### 第三種:f-string(推薦)
[PEP 498](https://www.python.org/dev/peps/pep-0498/)定義了最新的f-string,名稱為:**Literal String Interpolation**,可以在字串裡面直接使用變數,使用起來最直覺。
```
ver = 3.9
years1 = 18
years2 = 19
name = 'Aaron'
print(f'Python version: {ver}')
print(f'I\'m {years1} years old, and you are {years2}.')
print(f'Hello {name}!'
```
> 從今天起開始使用它。
> **注意**
>
> 字串的最前面要要加上`f`,表示這是一個f格式的字串。
#### r, u, b字串
##### r字串
r為raw的意思,字串前面加`r`代表這是一個不需要轉譯符號(例如:`\n`)的字串,所有的轉譯符號都會是普通的文字,例如:
```
print(r'這不是換行\n這不是換行')
```
會輸出:
```
這不是換行\n這不是換行
```
##### u字串
u為unicode的意思,表示這是一個unicode(國際通用字串編碼)的字串,通常用在像一個中文字需要多個位元組來表示的編碼,如果字串出現亂碼,則可以使用`u`字串,例如:
```
print(u'這是一個unicode字串')
```
> Python 3.x對unicode支援比較好,預設就是unicode,在Python 2.x的字串預設會是以bytes字串在運作。
##### b字串
b為byte的意思,代表這是一個位元組(byte)組成的字串,例如:
```
print(b'abc')
```
#### `'''`和`"""`包住的字串
三重引號可以用來表示多行字串,預設會==無視轉譯字元==並且像==換行或TAB之類的格式也都會被保留==,例如:
```
print('''
這是一個三重引號的字串
不需轉譯字元就可以換行
TAB也會被如實保留
''')
```
會輸出:
```
這是一個三重引號的字串
不需轉譯字元就可以換行
TAB也會被如實保留
```
> **注意**
>
> 從第一個三重引號開始就是第一行文字了,最後一個三重引號也會佔據一行,所以該範例總共會輸出五行的字串。
單純三重引號包圍住的文字並不會被執行,所以也常常被用來當作多行程式碼註解使用,例如:
```
"""
這是註解
這是註解
這是註解
"""
```
### 型態轉換
##### `int()`
如果想將字串、浮點數、布林等型態轉換成整數,可以使用`int()`函式來作轉換。
整數形態和字串型態無法直接做運算,就必須經過轉換。
例如:
```
print(3 + int('9'))
print(int('88') + 12)
```
輸出:
```
12
100
```
##### `bin()`、`oct()、hex()`
如果想直接輸出二進位、八進位、十六進位整數到畫面上,可以這樣做:
```
print(bin(10))
print(oct(10))
print(hex(10))
```
會輸出:
```
0b1010
0o12
0xa
```
其輸出的資料型態皆為字串,例如使用`type()`函式來得到其資料型態:
```
print(type(oct(10)))
print(type(hex(10)))
```
會輸出:
```
<class 'str'>
<class 'str'>
```
當我們使用`int()`函式從字串建立整數時,可以指定其基底為2進位、8進位或16進位。
例如:
```
print(int('10', 2))
print(int('10', 8))
print(int('10', 16))
```
會輸出:
```
2
8
16
```
需注意,如果要將`'3.14'`這樣的字串透過`int()`取得整數部分會出現`ValueError`錯誤,必須先使用`float()`轉為福點數後再用`int()`取得整數部分。
##### `float()`
可以將字串轉為浮點數型態。
##### `bool()`
`bool()`內可以傳入任何型態,只要傳入的是:`None、False、0、0.0、0j(複數)、''(空字串)`等等,都會回傳`False`,這些型態以外的的其他值傳入`bool()`,都會回傳`True`。
##### `str()`
可以將數值形態的資料轉換成字串型態。
##### `ord()`
顯示某個字元的編碼,例如:
```
ord('哈') #輸出:21704
```
##### `chr()`
將文字編碼轉為文字顯示,例如:
```
chr(21704) # 輸出:'哈'
```
##### 練習
ㄧ、下面有一程式,使用者輸入兩個數字,加總後顯示到標準輸出上:
```python
a = input('請輸入數字1:') # 程式會停在這裡等待使用者輸入
b = input('請輸入數字2:') # 程式會停在這裡等待使用者輸入
print('總和是: ' + a + b)
```
此程式執行結果為:
```
請輸入數字1:
3
請輸入數字2:
5
總和是: 35
```
正確執行結果應該為:
```
請輸入數字1:
3
請輸入數字2:
5
總和是: 8
```
請問該怎麼修改程式碼?
> 請使用轉型函式來解決問題
> 二、下面有一程式,使用者輸入一個帶有小數點的數字,保留小數點兩位後輸出到標準輸出:
```
a = input('請輸入一個小數數字: ')
print('保留小數兩位數: ' + a)
```
請問該怎麼修改程式碼?
> 請使用型態轉換來解答問題
## 群集型態
在開發過程當中,常常會需要收集一些資料後一起做處理,這些資料根據用途不同可能會使用不同的資料結構來做收集,例如可能會需要資料依照順序排列、需要資料不重複、需要每筆資料都有對應的的鍵值(key value)來做存取等等,相比其他程式語言需要使用特定的函式或類別來做建立,Python提供了語法上的直接支援,因此使用起來會更容易且方便。
### 清單(list)
清單的型態是list,其特色為:
- 有序(sorted)
- 索引(index)
- 內容可變
- 長度可變
清單可以使用`[ ]`來建立,裡面每個元素使用逗號「,」做區隔。
> 可以透過索引來取得指定元素值,起始為0,代表第一個元素,如:`a[0]`代表a的第一個元素值。
例如:
```
[1, 2, 3]
['Aaron', 'Apple', 'Amber', 'Astrid']
[3.4, True] # list內的元素可以是相異的資料型態
[] # 建立長度為0的清單
```
```
print([1, 2, 3])
print([1, 2, 3][0])
print([1, 'Aaron', True, 3.3, 'Today'])
print([1, 'Aaron', True, 3.3, 'Today'][4])
```
會輸出:
```
[1, 2, 3]
1
[1, 'Aaron', True, 3.3, 'Today']
Today
```
清單方法:
| 方法名稱 | 說明 |
| ----------- | -------------------------- |
| `append()` | 新增一筆資料到清單的最後面 |
| `remove()` | 移除清單內某個元素值 |
| `reverse()` | 將清單內元素順序反過來 |
| `sort()` | 排序清單 |
| `extend()` | 新增多筆資料到清單 |
例如:
```
a = [1, 2, 3,] # 最後一個逗號可以省略,如: [1, 2, 3]
a.append(999)
a.append(999)
a.append(999)
a.append('abc')
a.append(False)
print(a)
print(a[4])
a[4] = 'xxx'
print(a)
a.remove(999)
a.remove('abc')
a.reverse()
a = [3, 2, 6,0, 1, 9]
a.sort()
a.extend('abcde')
print(len(a))
a.append(0)
print(len(a))
print(a)
```
會輸出:
```
[1, 2, 3, 999, 999, 999, 'abc', False]
999
[1, 2, 3, 999, 'xxx', 999, 'abc', False]
11
12
[0, 1, 2, 3, 6, 9, 'a', 'b', 'c', 'd', 'e', 0]
```
範例2:
```
nums = [1, 2, 3]
print(nums)
nums.append(4)
print(nums)
print(nums[0])
nums[3] = 0
print(nums)
nums.remove(0)
print(nums)
```
輸出為:
```
[1, 2, 3]
[1, 2, 3, 4]
1
[1, 2, 3, 0]
[1, 2, 3]
```
###### 群集型態通用方法
通用方法可以使用在Python支援的各種群集型態上:
| 方法名稱 | 說明 |
| -------- | ------------------------------------ |
| `len()` | 取回該群基資料的長度(共有幾筆資料) |
| `del` | 刪除指定索引位置的元素 |
| `in` | 判斷資料是否存在於該群集資料中 |
> `remove()`方法和`del`都是刪除元素,但是`remove()`是根據給定的元素值來做判斷,而`del`是給定指定的位置,容易混淆,還請多加注意。
範例:
```
nums = [1, 2, 3]
del nums[0]
print(nums)
print(len(nums))
print(4 in nums)
```
輸出為:
```
[2, 3]
2
False
```
另外,你也可以使用`list()`方法來將其它群集資料轉成`list()`型態。
```
print(list('abcdef'))
```
會輸出:
```
[a, b, c, d, e, f]
```
> 可以使用`list()`函式來轉換成清單型態的資料必須是可迭代(iterable)的物件,像是字串、集合(set)或Tuple(tuple)等等,關於可迭代的的含義,之後的的章節會另外說明。
##### 練習
請將:
```
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
```
透過清單的操作,變成:
```
[8, 6, 4, 2, 0, 'ok']
```
並輸出到終端畫面上。
### 集合(set)
集合這個群集資料結構裡面的內容沒有順序且元素不會重複,可以使用`{ }`來建立集合,建立時每個元素以逗號「,」分開來,如果建立的時候有重複的元素,則只會保留一個,重複的都會被去除,例如:
```
print({'A', 'B', 'C'})
my_set = {1, 2, 3, 1, 2, 3}
print(my_set)
```
會輸出:
```
{'A', 'B', 'C'}
{1, 2}
```
如果要建立空的集合,並不是使用`{}`,這會建立一個空的字典(dict),而是要呼叫`set()`函式,例如:
```
my_set = set()
```
集合的方法:
| 方法名稱 | 說明 |
| ---------- | ---------------------------------------------- |
| `add()` | 新增一個元素 |
| `remove()` | 刪除元素 |
| `update()` | 新增多個元素,參數可以是list、set、tuple和字串 |
而上一小節提到的通用方法`len()、in`也可以用在集合上面,例如:
```
my_set = {1, 2, 3}
my_set.add(4)
my_set.add(3)
print(my_set)
my_set.remove(3)
print(my_set)
print(len(my_set))
print(3 in my_set)
```
會輸出:
```
{1, 2, 3, 4}
{1, 2, 4}
3
False
```
由於集合必須保證內容的不重複,所以並非所有資料型態的元素都可以放進去,例如:清單和集合就都不行。
```
{[1, 2, 3]}
{{1, 2, 3}}
```
會輸出:
```
TypeError: unhashable type: 'list'
TypeError: unhashable type: 'set'
```
要從清單、字串或Tuple建立集合,也可以使用`set()`函式,例如:
```
print(set('ABCDE'))
print(set([1, 2, 3]))
print(set((1, 2, 3)))
```
會輸出:
```
{'A', 'B', 'C', 'D', 'E'}
{1, 2, 3}
{1, 2, 3}
```
其他範例:
```
a = {0, 1, 2, 3, 1, 2, 3, 4, 5, 3, 4}
a = set()
print(a)
print(type(a))
a.add('A')
a.add('A')
a.add(True)
a.add(44)
a.remove(44)
print(a)
print(len(a))
a = [1, 2, 3]
a.append({'A', 'B'})
print(a[3])
```
會輸出:
```
set()
<class 'set'>
{True, 'A'}
2
{'A', 'B'}
```
清單跟集合可以互相轉換:
```
a = list({0, 1, 2})
print(a)
a = set([0, 1, 2])
print(a)
a = set('abc')
print(a)
```
會輸出:
```
[0, 1, 2]
{0, 1, 2}
{'a', 'c', 'b'}
```
##### 練習
請依序寫出下面需求的程式碼,執行後顯示第八項的結果到終端畫面上:
1. 建立一個清單,裡面有5個元素1, 2, 3, 4, 5,並指定給a變數。
2. 將清單第一個元素修改為0
3. 將清單最後一個元素修改為0
4. 將0從清單裡去除
5. 將清單由大到小排序
6. 將清單專換成集合
7. 加入2和3兩個數字到集合
8. 集合內現在有幾個元素?
### 字典(dict)
字典用來儲存兩兩對應鍵(key)與值(value),型態為dict,建立時可以使用`{ }`以及冒號「:」,鍵在左,值在右的格式,例如:
```
phones = {'Aaron': '+886-1234567', 'Amber': '+886-7654321'}
print(phones['Aaron'])
```
上面範例會建立兩筆dict資料,'Aaron'和'Amber'為鍵,'+886-1234567'和'+886-7654321'為各自的值;透過鍵來取得值,該範例會輸出:
```
+886-1234567
```
> 注意:
>
> 1. 鍵不可以重複。
> 2. 透過`[]`來取得鍵所對應的值。
> 3. `del`可以刪除某個鍵與值。
> 4. `in`可以判斷該鍵是否存在。
如果透過`[]`取出值時該鍵不存在,會發生`KeyError`的錯誤,可以使用`in`先判斷該鍵是否存在,例如:
```
data = {'A': 123, 'B': 456, 'C': 789}
print(data)
del data['B']
print(data)
print('B' in data)
```
會輸出:
```
{'A': 123, 'B': 456, 'C': 789}
{'A': 123, 'C': 789}
False
```
字典的方法:
| 方法名稱 | 說明 |
| ---------- | ---------------------------------- |
| `get()` | 取得指定鍵的值 |
| `items()` | 取得每一對鍵值,會回傳`dict_items` |
| `keys()` | 取得所有的鍵,會回傳`dict_keys` |
| `values()` | 取得所有的值,會回傳`dict_values` |
```
data = {'A': 123, 'B': 456, 'C': 789}
print(data.items())
print(data.keys())
print(data.values())
```
會輸出:
```
dict_items([('A', 123), ('B', 456), ('C', 789)])
dict_keys(['A', 'B', 'C'])
dict_values([123, 456, 789])
```
除了使用`{}`建立字典外,也可以使用`dict()`和`fromkeys()`函式來建立字典,例如:
```
data = dict(A = 123, B = 456, C = 789)
# 等同於data = {'A': 123, 'B': 456, 'C': 789}
data2 = data.fromkeys(['A', 'B'], '000')
print(data)
print(data2)
```
會輸出:
```
{'A': 123, 'B': 456, 'C': 789}
{'A': 000, 'B': 000}
```
> 透過dict()方法建立字典,鍵不需要用大括號包圍,且不能給重複的鍵。
##### 練習
```
a = {'A': 1, 'B': 2, 'C': 3}
```
1. 將鍵為'B'的值改為5
2. 將鍵為'C'的值轉型成字串
3. 刪除鍵為'A'的該筆資料
4. 新增鍵'D',設定其值為99
5. 新增鍵'E',設定其值為一清單,該清單內有2和3兩個值
6. 計算a這個字典目前有幾組元素
7. 取出a字典內所有的值,用清單存放,並設定給b變數
答案:
```
a['B'] = 5
print(a)
a['C'] = str(a['C'])
print(a)
del a['A']
print(a)
a['D'] = 99
print(a)
a['E'] = [2, 3]
print(a)
print(len(a))
b = list(a.values())
print(b)
```
### Tuple(tuple)
Tuple很多地方跟list類似,如:有順序性、可以使用`[]`來存取元素,但是Tuple在建立後就不可以修改內容,建立Tuple有兩個直接支援的語法:直接在某個值後面加上「逗號`,`」或是使用一對小括號`()`,例如:
```
a = 10,
print(a)
b = 10, 20, 30
print(b)
c = 'Aaron', 10, True
print(c)
d = (100, 101, 102)
print(d)
print(d[0])
type(d)
```
會輸出:
```
(10, )
(10, 20, 30)
('Aaron', 10, True)
(100, 101, 102)
100
<class 'tuple'>
```
> 1. 雖然用逗號就可以建立Tuple,但是用小括號的話更可以讓人容易一眼就看出其型態。
>
> 2. 建立只有一個元素的Tuple不可以使用小括號,而要使用逗號,例如: `(elem)`不行,`elem, `可以。
> 3. 事實上,之前在建立list、set、dict時也都是省略了最後一個逗號,所以如果之後有看到逗號,就不用太訝異。
Tuple內的元素可以做拆解(Unpack)指定給不同的變數,例如:
```
a, b, c = (10, 20, 30)
print(a)
print(b)
print(c)
```
會輸出:
```
10
20
30
```
因為Tuple的括號是可以省略的,所以有可能會看到下面這樣的寫法:
```
a, b, c = 10, 20, 30
a, b = b, a
print(a)
print(b)
```
會輸出:
```
20
10
```
> 拆解元素的特性同樣可以用在list、set上面,例如:`x, y, z = [1, 2, 3]`或`x, y, z = {1, 2, 3}`
除了拆解(Unpack)元素之外,還有個進階拆解元素功能(Extended Iterable Unpacking),例如:
```
a, *b = (1, 2, 3, 4, 5)
print(a)
print(b)
a, *b, c = [1, 2, 3, 4, 5]
print(a)
print(b)
print(c)
```
會顯示:
```
1
[2, 3, 4, 5]
1
[2, 3, 4]
5
```
在某個變數上指定`*`後,其他變數會被分配單一值,剩下的元素會以list的型態指定給標上星號的變數。
```
a = (1, 2, 3)
# b = a[0]
# c = a[1]
# d = a[2]
b, c, d = a # 等同上面三行
print(b, c, d)
b, c, d = [4, 5, 6]
print(b, c, d)
b, c, d = {7, 8, 9}
print(b, c, d)
b, c, *d = 1, 2, 3, 4, 5, 6
print(b, c, d)
b, *c, d = 1, 2, 3, 4, 5, 6
print(b, c, d)
```
會輸出:
```
1 2 3
4 5 6
8 9 7
1 2 [3, 4, 5, 6]
1 [2, 3, 4, 5] 6
```
### 群集型態快速比較
| | List | Set | Dict | Tuple |
| -------- | :----------------: | :------: | :--------------: | :---------: |
| 建立 | []、list() | set() | {}、dict() | ()、tuple() |
| 新增資料 | append()、extend() | add() | [] | n/a |
| 查詢 | [] | n/a | get()、[] | [] |
| 修改 | [] | n/a | [] | n/a |
| 刪除 | remove()、del | remove() | del | n/a |
| 判斷存在 | if-in | if-in | has_key()、if-in | if-in |
## 變數與運算子
### 變數
在寫程式時,我們會經過很多運算來得到結果,香蕉每斤20元,想要得到10斤、20斤、30斤要多少錢,例如:
```
print('香蕉10斤價格:', 20 * 10)
print('香蕉20斤價格:', 20 * 20)
print('香蕉30斤價格:', 20 * 30)
```
一週後香蕉每斤價格變動了,必須每一行修改程式碼,如果說程式裡有10個地方都需要計算香蕉的價格,那就必須修改10個地方,如果漏掉一個地方,程式的執行結果就會出現錯誤。是不是有個更方便的辦法可以更快的修改並且降低錯誤呢?答案就是使用「變數」,請看下面程式碼:
```
banna_price = 20
print('香蕉10斤價格:', banna_price * 10)
print('香蕉20斤價格:', banna_price * 20)
print('香蕉30斤價格:', banna_price * 30)
```
這裡的執行結果會和上一次的程式碼一樣,但是,未來香蕉每斤價格改變了,只需要修改`banna_price`後面的數字,不管有多少行,因為全部都會使用`banna_price`內存放的20來做計算,所以都會跟著反映在執行結果上,減少了修改可能造成的錯誤,並且幫20取了一個有意義的名字,而非不明意義的魔術數字。
`banna_price`被稱為變數(variable),透過等號「=」號,我們將20存到變數裡面,這個變數的值就相當於20,之後,就可以直接拿變數來做運算。
Python屬於動態型別語言,變數本身沒有型態,且可以不需要經過宣告,在第一次透過「=」號指定值的時候就是建立了一個變數,如果在建立之前,嘗試存取變數,會出現`NameError`的錯誤,例如:
```
print(x)
```
會輸出:
```
NameError: name 'x' is not defined
```
##### 變數命名規則
1. 可以使用英文字母大寫及小寫,大小寫代表不同的變數。
> 例如:`a`和`A`是不同的變數名稱。
2. 可以使用數字,但第一個字不能是數字。
> 例如:`1abc`為錯誤命名,`abc1`為合法名字。
3. 可以使用底線,第一個字為底線的話,有其特殊意義,請了解後再使用。
> 例如:
>
> - 底線開頭的變數通常為隱藏的變數,不打算給外部使用。
> - 開頭連續兩個底線的變數,代表其唯一性,不可被修改。
4. 不可以使用保留字。
> 保留字:
>
> If、elif、else、and、or、not、import、from、as、assert、for、while、break、continue、try、except、finally、pass、def、return、lambda、del、global、nonlocal、in、is、with、yield、class、None、True、False
##### `id()`
Python內,變數始終都會是參考(reference),透過等號只是把參考指定給變數,可以呼叫`id()`函式來取得變數的記憶體位址,例如:
```
x = 10
y = x
print(id(x))
print(id(y))
```
可能的輸出如下(因電腦不同,記憶體位址可能會與這裡的輸出不同,但兩個變數的記憶體位置會一樣):
```
289012347
289012347
```
一開始`x`參考到了10這個整數物件,也就是`x`裡存放的是10的物件參考,接著把`x`內存放的參考也放到`y`變數內,所以`y`變數也參考到10物件,也就是同一個物件。
在看下面範例:
```
x = 1
print(id(x))
x = x + 1
print(id(x))
```
會輸出:
```
18090383000
18094327699
```
一開始`x`參考到1這個物件,接著`x`參考的內容加上1之後會產生一個新的物件,然後透過等號將新的物件參考在指定給`x`,`x`內舊的參考會被覆蓋成新的參考。
在看下面範例:
```
x = [1, 2, 3]
print(x)
y = x
y[0] = 0
print(x)
```
輸出:
```
[1, 2, 3]
[0 ,2, 3]
```
在指定`y = x`的時候,兩個變數因為參考到同一個物件,所以將`y[0]`改為0後,原本的`x`內的元素0也跟著改變了。
##### `is、is not`
除了使用`id()`函式來觀察變數參考的記憶體之外,還可以使用`is`指令來確認兩個變數是否參考到同一物件,用`is not`來判兩個變數參考是否不一樣。
```
x = 3
y = x
print(x is y)
y = 20
print(x is y)
```
會輸出:
```
True
False
```
變數本身沒有型態,可以對著同一個變數指定不同形態的值,當呼叫變數的某個方法的時候,只要確定方法存在就不會出現錯誤,例如`list`和`tuple`兩種型態都有查詢元素位置的`index()`方法,因此下面的程式碼不會出錯:
```
x = [1, 2, 3]
print(x.index(2))
x = (1, 2, 3)
print(x.index(2))
```
當不再需要某個變數,可以呼叫`del`來刪除它,例如:
```
x = 10
del x
print(x)
```
會輸出:
```
NameError: name 'x' is not defined
```
會出現錯誤,因為`x`變數已經被刪掉了。
### 算數運算子
算術運算子有:
| 運算子 | 說明 |
| ------ | ---------------------- |
| + | 加法運算 |
| — | 減法運算 |
| * | 乘法運算 |
| ** | 指數運算 |
| / | 除法運算 |
| // | 除法運算,但只保留整數 |
| % | 除法運算,但是只取餘數 |
##### 數值的運算
```
print(1 + 2)
print(1 - 2)
print(1 * 2)
print(2 ** 3)
print(1 / 2)
print(1 // 2)
print(1 % 2)
```
會輸出:
```
3
-1
2
8
0.5
0
1
```
需注意小數點的運算可能會出乎意料:
```
print(0.1 + 0.1 + 0.1)
print(1.0 - 0.8)
print(0.1 + 0.1 + 0.1 == 0.3)
```
會輸出:
```
0.30000000000000004
0.19999999999999996
False
```
這並不是Python程式語言的錯誤,而是Python遵守IEEE 754浮點運算標準,而這個運算標準因為使用分數和指數來表示,所以會有浮點數誤差。
如果對小數運算的精度要求很高的話,可以使用`decimal.Decimal`類別來做運算,例如:
```
import decimal
d1 = decimal.Decimal('0.1')
d2 = decimal.Decimal('0.1')
d3 = decimal.Decimal('0.1')
print(d1 + d2 + d3)
```
> 在指定數字的時候,必須使用字串,但可以直接使用算術運算子做運算
如果對著布林值做運算,`True`會被當作1,`False`會被當作0,然後再進行運算,例如:
```
print(True+False)
```
會輸出:
```
1
```
##### 字串的運算
使用「+」可以用來串接運算,使用「*」可以重複字串,例如:
```
t1 = 'Hello'
t2 = 'Aaron'
print(t1 + t2)
print(t1 * 5)
```
會輸出:
```
HelloAaron
HelloHelloHelloHelloHello
```
字串因為本身不可以被修改,所以每次運算後都是產生新的字串。
Python本身偏向強型別,也就是不同型態之間在做運算時,比較不會進行自動轉換,所以字串和數字不能進行+運算,如果要進行串接,就必須先把數字轉成字串,如果要進行算數相加,就必須將字串先轉成數字,例如:
```
print('10' + str(3))
print(10 + int('3'))
```
會輸出:
```
103
13
```
##### `list、tuple運算`
`list`使用「+」運算子可以進行串接,使用「*」,例如:
```
n1 = ['one', 'two']
n2 = ['three', 'four']
print(n1 + n2)
print(n1 * 2)
```
會輸出:
```
['one', 'two', 'three', 'four']
['one', 'two', 'one', 'two']
```
「+」和「*」運算在`tuple`上也有相同的效果,雖然`tuple`本身不能變動,但是當中的元素是可以變動的,例如:
```
n1 = [1, 2]
n2 = [3, 4]
nn = (n1, n2)
print(nn)
n1 = 5, 6
print(nn)
```
會輸出:
```
([1, 2], [3, 4])
([5, 6], [3, 4])
```
##### 練習
1. 將:
```
a = [3, 4]
b = [5, 6]
```
轉換成:
```
[3, 4, 5, 6, 3, 4, 5, 6]
```
並存放到result變數裡
2. 將:
```
c = [3.3, 6.6, 9.9]
```
轉換成:
```
[9, 6, 3]
```
並存放到result變數裡
### 比較運算子
| 比較運算子 | 說明 |
| ---------- | ------------------------------------------ |
| > | 大於 |
| >= | 大於或等於 |
| < | 小於 |
| <= | 小於或等於 |
| == | 等於 |
| != | 不等於 |
| <> | 不等於(與!=功能相同)==Python 3已不支援== |
> `<>`效果與`!=`相同,其只是為了相容性而存在,這裡列出只是為了將來見到不至於迷惘,請盡量使用`!=`來判斷,而不要使用`<>`,且在Python 3也已經不支援。
比較的結果,不是`Ture`就是`False`,代表成立或不成立。
> 請勿將`==`、`!=`與`is`、`is not`搞混,前者是比較物件真實的值,而後者是比較兩個物件的參考是否相同
```
x = [2, 3]
y = [2, 3]
print(x == y)
print(x is y)
```
會輸出:
```
True
False
```
除了數字之外,字串、`list`、`set`、'tuple'和`dict`也都可以進行比較運算,其運算依從第一個元素依照順序做比較,而字元的比較為比較其字元碼(如英文字為ASCII碼),例如:
```
print('AAC' < 'ABC')
print([2, 3] > [4, 5])
print((2, 3) == (4, 5))
print({2, 3} > {4, 5})
print({'A': 3} == {'A': 3)
```
會輸出:
```
True
False
False
False
True
```
### 指派運算子
除了目前已經用過的「=」指派運算子之外,其它的如下:
| 指派運算子 | 範例 | 等同於 |
| ---------- | ------- | ---------- |
| += | a += b | a = a + b |
| -+ | a -= b | a = a - b |
| *= | a *= b | a = a * b |
| /= | a /= b | a = a / b |
| //= | a //= b | a = a // b |
| %= | a %= b | a = a % b |
| &= | a &= b | a = a & b |
| \|= | a \|= b | a = a \| b |
| ^= | a ^= b | a = a ^ b |
| <<= | a <<= b | a = a << b |
| >>= | a >>= b | a = a >> b |
### 邏輯運算子
| 邏輯運算子 | 說明 |
| ---------- | ------------------------------------------------ |
| and | 且,左右運算元都必須為真,其結果才為真,否則為假 |
| or | 或,左右兩邊只要有一運算元為真,其結果即為真 |
| not | 反相 |
```
print(True and True)
print(True and False)
print(True or False)
print(not True)
```
會輸出:
```
True
False
True
False
```
將`None、False、0、0.0、0j、''、()、[]、{}`等傳入`bool()`都會是`False`,其它值都會是`True`,這個原理也同樣套用在邏輯運算子上。
Python的邏輯運算子有捷徑運算的特性,也就是其運算的判斷由左至右,只要結果確立了,就會回傳確定結果位置的值。
`and`運算子左運算元如果為假,就可以確認邏輯不成立,因此可以不用繼續運算右運算元;`or`左運算元為真,則可以確定邏輯成立,因此可以不用運算右運算元,當判斷停留在哪裡,就會回傳該位置的運算元為結果值,例如:
```
print([] and 'Aaron')
print([1, 2] and 'Aaron')
print([] or 'Aaron')
print([1, 2] or 'Aaron')
```
會輸出:
```
[]
Aaron
Aaron
[1, 2]
```
### 位元運算子
| 位元運算子 | 說明 |
| ---------- | ------------------------------------------ |
| & | AND運算 |
| \| | OR運算 |
| ^ | 互斥OR運算 |
| ~ | 補數運算 |
| >> | 右移運算(左邊原來是0則補0,是1就補1) |
| << | 左移運算(右邊會補上0) |
位元運算即是逐個位元做運算,例如:
```
print(0b10010001 & 0b01000001)
print(0b10010001 | 0b01000001)
print(~0b11)
print(1 << 1) # 等同於2的1次方
print(1 << 3) # 等同於2的3次方
```
會輸出:
```
1
209
-4
2
8
```
位元運算子除了數值的運算之外,還可以用在`set`上做交集(&)、聯集(|)、對稱差集(^)以及差集(-),並且可以用比較運算子(`>、<、>=、<=、==`)來比較兩個`set`的包括關係。
> **補充**
>
> 交集:只保留兩者皆有的元素,且只保留一份。
>
> 聯集:兩者的元素合併,重複的元素只保留一份。
>
> 對稱差集:兩者皆有的元素去掉,只保留兩邊互相沒有重複的元素。
>
> 差集:前者有,但後者沒有的元素。
例如:
```
a = {1,2,3,4,5}
b = {2,3,4,5,6}
print(a & b) # 交集
print(a | b) # 聯集
print(a ^ b) # 互斥
print(a - b) # 差集
```
會輸出:
```
{2, 3, 4, 5}
{1, 2, 3, 4, 5, 6}
{1, 6}
{1}
```
### 索引切片運算
在Python中,只要具有索引特性的型態,基本上都可以使用切片(slice)運算,像是字串、list、tuple等等,透過切片運算,可以取得其內容之中的某一個片段,例如:
```
name = 'Aaron'
print(name[0:3])
print(name[3:])
print(name[:2])
print(name[:])
print(name[:-1])
print(name[-3:-1])
```
輸出為:
```
Aar
on
Aa
Aaron
Aaro
ro
```
重點如下:
1. 其索引格式為:[start, end],start會包含其指定的索引值,但end指定的索引值不包含,索引從0開始代表第一個元素。
2. 如果是負數,表示從後面數,-1代表倒數第一個元素。
3. 如果冒號前面沒有指定,表示從頭開始,如果冒號後面沒有指定,表示最後一個元素。
切片運算另一個形式是:`[start:end:step]`,step代表每次間隔的內容,例如:
```
data = '1234567890'
print(data[::2])
print(data[::-1])
```
會輸出:
```
13579
0987654321
```
負數偏移表示以負偏移1的方式來取字串,結果就會變成反轉字串。