---
# System prepended metadata

title: Python Learning Note
tags: [python]

---

---
title: Python Learning Note
tags: python
---
[TOC]

## Fundamental Concept
1. Python為一[強型別程式語言](https://stackoverflow.com/questions/11328920/is-python-strongly-typed)
     
     ![](https://i.imgur.com/b58Zw3U.png)


2. Python的語法中沒有`{}`，因此是用縮排(indent)來判別範圍，縮排並沒有規定為多少空格，只要統一即可

3. Python中所有的東西都是物件，變數實際上是參考到一個物件(可以用id查看)
    - Immutable object：此物件的內容不能被更改，所以對此物件的更改實際上是創造一個新的物件，並將新物件的參考assign給變數
        - 傳遞參數時**類似C/C++ call by value**
        - 常見的有: int、str、tuple
    - Mutable object：此物件內容可以被更改，因此每次對其做的更改，都對該物件的參考做更改
        - 傳遞參數時**類似C/C++ call by reference**
        - 常見的有: list、dict
    - 這個觀念非常重要，一開始初學時因為不知道所以很常碰壁

4. 註解符號
    - Single Comment: `#後面內容為單行註解`
    - Mutiple Comment: `""" 內容為多行註解 """`

5. Keyword
    1. `pass`
        - 如同字面上的意思，當程式運行到 pass statement 時，會略過
        - 非常常見，像是一開始規劃程式時，可以列出需要的function，但function body不能為空，此時就可以用pass略過；或是在處理Exception時，有時遇到一些例外可以略過不處理，也會用到pass
    2. `is`
        - 用來判斷兩物件是否相等，跟`==`不同的是，`is`比較是兩物件的記憶體位址，`==`比較的是兩物件的值
        - 為什麼要有`is`和`==`，事實上跟第三點有關，如果看到這裡還無法明白且暫時不去思考，看完整篇筆記就會知道了


## Control Statement
### If Satement
```python=
if condition:
    handle statement
elif condition:
    handle statement
else:
    handle statement
```

Condition 的部分可以加括號亦可不加

### Ternary Operator
:::danger
Python 沒有 Ternary Operator
:::

```python=
# 第一種方法
True Satement if Condition else False Satement
# 第二種方法
(True Satement, False Satement)[Condition]
# 歡迎提供更多方法
```

### Switch Statement
:::danger
Python 沒有 switch statement
:::
替代方案有蠻多種，這裡提供一個我自己用過還不錯的
```python=
# 使用 dict 模擬 switch
# 使用時機為 內容行為可以直接用function處理

# 為不影響篇幅，假設fun已存在    
handler = {
    '1' : fun1,
    '2' : fun2,
    '3' : fun3,
}

# 執行
fun_handle = handler[choice]
fun_handle()
# 簡化
handler[choice]()
# 更好的寫法
handler.get(choice, lambda:print('No option'))()
```

## Loop
### While Loop
```python=
# 注意縮排
while Condition:
    statement
```

### For Loop
?? python的for好像是用到iterator、generator的概念，這個我還不是很清楚，先用例子記起來吧
```python=
# Increment
for i in range(5):
    print(i, end=' ')  # 0 1 2 3 4

for i in range(2, 5):
    print(i, end=' ')  # 2 3 4

for i in range(1, 5, 2):
    print(i, end=' ')  # 1 3

# Decrement
for i in range(5, 0, -1):
    print(i, end=' ')  # 5 4 3 2 1

# List & Tuple
listA = ['A', 'B', 'C']
for idx in range(len(listA)):
    print(listA[idx])     # A B C

for elem in ['A', 2, 'C', 4]:
    print(elem, end=' ')  # A 2 C 4

for key, value in [(1, 'one'), (2, 'two')]:
    print(f'{key}={value}', end= ' ')  # 1=one 2=two
```
## Function
### Basic Function Prototype
```python=
def FunctionName(Parameter List):
    Function Body
```
### Return None Type
這邊需要注意的是，function可以不回傳東西，因此若沒有明確指定要回傳甚麼，都是回傳 `None` 這個特殊型別
```python=
# 以下三種寫法回傳值皆為 None
def fun1():
    print('Hello')
    
def fun2():
    print('Hello')
    return

def fun3():
    print('Hello')
    return None
```

### Multiple Return Values
Python 允許同時回傳多個回傳值，以**Tuple**形式回傳
```python=
def fun(a, b, c):
    return a, a+b, a+b+c # 等同於 return (a, a+b, a+b+c)

n          = fun(1,2,3)  # n = (1,3,6)
n1, n2, n3 = fun(1,2,3)  # n1=1, n2=3, n3=6
n1, n3     = fun(1,2,3)  # Try and Test(T1)
```

### Parameter has Default Value
Python 允許有Default Value，規則是通用的一律從右邊開始省略
```python=
def fun(a, b=5, c=10):
    return a+b+c

n1 = fun(1,2,3)  # n1=6
n2 = fun(1,2)    # n2=13
n3 = fun(1)      # n3=16
```

### Position & Keyword Arguments
在Python傳遞參數時，除了用位置對齊以外，還可以用parameter的名字來指定
注意：**position argments 不能在 keyword arguments 的右邊**

```python=
def say(name, msg):
    print(name, msg)

say('Tom', 'Hi') == say(msg='Hi', name='Tom') == say('Tom', msg='Hi')
```

### Advanced Function Prototype
> 你可以不會寫，但是遇到必須看得懂

```python=
def FunctionName(arg, *args, **kwargs):
    Function Body
```

`*args`：允許傳入可變動position參數，儲存資料型態為 tuple
`**kwargs`：允許傳入可變動keyword參數，儲存資料型態為 dictionary
> 若要同時使用則 `*args` 需要在 `**kwargs` 的左邊

```python=
def fun(arg, *args, **kwargs):
    print(f'arg = {arg}')
    print(f'args = {args}')
    print(f'kwargs = {kwargs}')    

# 執行函數
fun(1, 2, 3, k1=4, k2=5)

# 觀察輸出結果
Output:
arg = 1                     # Position Argument
args = (2, 3)               # Mutable Position Argument, which type is Tuple
kwargs = {'k1': 4, 'k2': 5} # Mutable Keyword Argument, which type is dictionary
```

對於`*args、**kwargs`該如何運用?
你只需要記得他們的型別為何，就可以運用自如

```python=
# *args mutable position arguments
def fun(*args):
    sum = 0
    for num in args:
        sum += num
    print(f'sum is {sum}')

# Try & Test(T2)
# 這樣的寫法非常蠢，想想看有沒有更簡短的寫法
fun(1,2,3,4,5,6,7,8,9,10)
# 如果我只想加1~10的奇數就好，該怎麼寫
# (我用到List Comprehension，還沒看到可以跳過)

Output:
sum is 55
```

```python=
# **kwargs mutable keyword arguments
def fun(**kwargs):
    for key, value in kwargs.items():
        print(f'({key}, {value})')

fun(k1='one', k2='two', k3='six')

Output:
(k1, one)
(k2, two)
(k6, six)
```

傳遞參數時可以使用`*args、**kwargs`

```python=
def fun(a1, a2, a3):
    print(f'a1={a1}, a2={a2}, a3={a3}')

# 設定變數
nums = (1,2,3)   # Try & Test 將Tuple改成List(T3)
kwnums = {'a3' : 6, 'a1' : 4, 'a2' : 5}

# 傳遞給函式，這樣的寫法有一種將它展開的感覺
fun(*nums)
fun(**kwnums)

Output:
a1=1, a2=2, a3=3  # fun(*nums) result
a1=4, a2=5, a3=6  # fun(**kwnums) result
```
上面的例子唯一要注意的是，`kwnums`中的key必須為**字串**

### Lambda Function
> 這招必學，學會程式碼可以非常簡短!!

#### Lambda Syntax
```python=
lambda [arg1 [, arg2 [,..., argN]...]] : Lambda Body
```
關於Lambda的一些重點:
1. Lambda 為一匿名函數
2. Lambda 的參數可以有或沒有
3. Lambda Body~~只能有一行~~ one expression(?? diff expression statement)
4. Lambda Body不需要寫Return
    - 事實上，可以看成`lambda args : return Lambda Body`
    - 一樣的概念，如果你的Body像是print()，就是回傳None
5. Lambda Body不能做Assign
    - ?? 目前不清楚為什麼，剛好寫程式有碰到

繼續之前，釐清觀念會更好(內含我自己的推測，請不要全盤接受，盡量挑錯):
> 還記得之前說過，變數為一物件的參考(在Fundamental Concept 第三點)。
> 經過我的觀察，函數也可以用相同的概念來看。
> 

```python=
# 先進到Python Interpreter
def f():
    return 1

# type f
<function f at 0x00000217D65C52F0>
# 網路上沒找相關資訊但我推測後面的數字應該會是記憶體位置
# 我是由 0x + 碼數16位 + python interpreter 64 bits 判斷

# 可以看到函數的行為是被存在一個固定的地方
# f 只是一個變數，它參考到這塊記憶體位址

# 進行以下測試
g = f
f = 0
# type g
<function f at 0x00000217D65C52F0>
# type f
0
# type g()
1
# 其實可以看到我們的function name是甚麼根本沒差
# 重要的是只要參考到我們要的記憶體位址(或是說函式物件)，就夠了
# 額外補充()在此用作 function call operator
```
:::info
結論:
funcion name如同變數一樣，它們都是一個名稱，都可以參考到一個物件，
只是funcion name一開始被定義時，稍微特別一點，它參考到的物件可以執行`()`
:::

進入主角
```python=
# 先進入Python Interpreter

# 這個函式行為跟上面是一樣的，希望可以看得出來
# type lambda : 1 
<function <lambda> at 0x00000217D65C5488>

# 到這裡，你可能已經看出來了
# 當我們輸入 lambda : 1 時
# 事實上就是定義了一個函式的行為，並且已經被存在某個記憶體空間了
# 剛剛說了名字是甚麼根本不重要，重要的是我們需要掌握這一塊記憶體位址，才能執行函數行為
# 因此底下提供兩種做法

# 1. 這個函式只會在"現在"用到，並且只會使用一次，那就直接呼叫吧
(lambda : 1)()

# 2. 這個函式"未來"會用到，給它一個變數名稱吧
f = lambda : 1
f()

# 發現了嗎，第二點的使用方法跟以前一模一樣阿
# 難道是我在跟你開玩笑?
# 不，能用一行解決的事，為什麼要寫兩行
```
:::info
結論:
就跟你說學會這招可以簡短程式碼了吧。
思考一下如果有參數的lambda，用兩種寫法該怎麼表示。
不要往下滑，有答案!!
:::

Lambda Example
```python=
# 該筆記的Data Structure中List、Dict
# 使用sort時，很常用到Lambda，底下都有範例
# 在此不贅述

seq = [-4, -3, 5, 7, 9]

filter_res = list(filter(lambda x : x < 0, seq))
# filter_res = [-4, -3]

map_res = list(map(lambda x : x**2, seq))
# map_res = [16, 9, 25, 49, 81]

# 未來寫視窗會用到
# 在tkinter中的按鈕，預設command是不能帶參數的
# 如果要帶參數怎麼辦?
def btn_handler(arg):
    pass
btn = tkinter.Button(..., command=lambda:btn_handler(x)).pack()

# 公布解答
# 1. 帶參數直接呼叫
(lambda x, y, z : x*y*z)(3,6,9)
# 2. 存起來日後可以用
f = lambda x, y, z : x*y*z
f(3,6,9)
```




### Try & Test
T1: Raise **ValueError**, Message is "too many values to unpack"
> 所以如果不知道會回傳幾個參數，就用一個變數去取，型別為Tuple；不然就必須要用剛好的變數去取

T2: 參考寫法，歡迎貢獻更多不一樣的寫法
```python=
fun(1,2,3,4,5,6,7,8,9,10)
# 改成
fun(*tuple(range(11)))
# 只想加奇數
fun(*[x for x in range(11) if x%2])
```
T3: 答案是沒有問題
> 按照我的理解，list & tuple的差別應該只是
> list is mutable
> tuple is imutable

### Inbulit Function
1.  `abs(num)`
    - Return the absolute value of `num`
2.  `sum(iterable, start=0)`
3.  `id(obj)`
    - Return a **unique** number(not address) about the `obj`
4.  `type(obj [, base, dict])`
    - `type(obj)` Return the class name of the obj
    - `type(obj, base, dict` Create a new class
5.  `int(n) float(n)`
6.  `oct(n) hex(n)`
    - Return type is string
7.  `complex(real=0, imag=0)`
8.  `str(x) bytes(x)`
9.  `list(iterable) tuple(iterable) dict(iterable)`
    - 底下Inbuilt Data Structure有介紹
10. `isinstance(obj, class | tuple)`
    - 可以判斷obj是否為該class或是該class的子類別
    - Return boolean


## I/O
### Simple I/O
```python=
'''Input''' 
# 讀取資料，直到EOF，回傳型別為字串
data = input()
data = input('Input Prompt')

'''Output'''
print(var1, var2, var3, sep='&')   # 預設會自動隔一個空格，透過sep指定
print(var1, var2, end='') 
# 預設print一次會自動接一個\n，可以透過end指定以空字串結尾(kwargs的用法)
```

### File I/O
1. 開啟檔案: 使用 open 內建函式
    - [更詳細的參數用法](https://docs.python.org/3/library/functions.html?highlight=open#open)
    - 基本會用到的參數
        - file
            - 必要
            - 為一絕對、相對的檔案路徑
            - 型別為字串
        - mode
            - Default is `'r'`
            - 非必要，但一般都會填
            - 開啟檔案的模式
                - `'r'`  : read only
                - `'r+'` : read and write, no Truncation
                - `'w'`  : write only, Truncation
                - `'w+'` : read and write, Truncation
                - `'a'`  : append, always append before EOF
                - `'b'`  : binary
                    - `'rb'`、`r+b`   : no Truncation
                    - `'wb'`、`'w+b'` : Truncation
            - 型別為字串
        - encoding
            - 非必要，偶爾會需要用到
            - 型別為字串
    - 回傳一檔案物件，利用此物件進行File I/O
> Truncation : 開啟檔案時會把原本檔案刪掉

2. 檔案物件(以下簡稱 f_obj)
    - 常用屬性
        - `f_obj.closed`
            - Return Ture if f_obj is closed
        - `f_obj.name`
            - Return file name
        - `f_obj.mode`
            - Retrun file open mode
    - 常用函式
        - `f_obj.close()`
            - 關閉檔案物件
            - 當檔案物件的參考(變數)參考到其他檔案物件時，python會自動關閉檔案
        - `f_obj.read([count])`
            - 讀取檔案內容
            - count 為要讀取多少 bytes
            - 沒有指定參數，則會盡可能地讀全部的資料
            - Return string
            - Return `''` if reached EOF
        - `f_obj.readline()`
            - 讀取資料直到`'\n'`
            - Return string
            - Return `''` if reached EOF
        - `f_obj.readlines()`
            - 讀取整個檔案
            - Return **list**
            - 等同於 `f_obj.read().split('\n')`
            - 等同於 `list(f_obj)`
        - `f_obj.write(string or bytes)`
            - 將資料寫入檔案
            - 寫入的資料不是string就是bytes(看模式)
            - Return bytes which are written successfully

```python=
# 分享一個曾經看過的寫法
f_obj = open('example.txt', 'r')

for line in f_obj:
    print(line, end)

f_obj.close()
```
這樣的好處我想應該是可以節省資料的儲存空間

## Class and Object
### Basic Class Prototype
```python=
class ClassName(object):
    def __init__(self, args):
        # Initialize data members
        self.public_data       # No     underline
        self._protected_data   # Single underline
        self.__private_data    # Double underline
    def __del__(self):
        # Destructor
        pass
    def func(self):
        # Define member function
        pass
    def __private_func(self):
        pass
```
1. Class 一般名稱開頭會大寫
2. Class 的每個member function第一個參數一定要是self
    - self 指的就是該類別本身
    - 在class內部存取自己的data member都要透過self
3. `__init__` 相當於建構元，用於初始化data member
    - 在Python建立物件的流程，事實上是，`__new__ -> __init__`
4. `__del__` 相當於解構元(我還沒用過)
5. 關於`public、protected、private`
    - Python 並沒有實作太多這樣的存取機制(protected 不常見到，一般最多就publice、private)
    - Default 都是public
    - 若要private，前面加雙底線
6. `class ClassName(object):` 可以寫成 `class ClassName:`
    - 後面繼承會談到

```python=
def getGender(self):
    return self.gender

class Person:
    """ This is Person class description """
    def __init__(self, name, gender, age=18):
        self.name   = name
        self.gender = gender
        self.__age  = age
    
    # 提供我知道的一些方法
    def showName(self):
        print(self.name)
    getAge = lambda self : self.__age
    getGender = getGender

Mary = Person('Mary', 'woman', 20)

# Class 往下如果是一個多行註解，它會變成該class的說明文字
# 也可以在help(Person)、help(Mary)查看該說明內容
print(Person.__doc__, Mary.__doc__)
# 示範使用member function
Mary.showName()
print(Mary.getGender(), Mary.getAge())
# Raise AttributeError
print(Mary.__age)        
```

### Inheritance

```python=
# Single Inheritance
class SuperClass:
    pass

class SubClass(SuperClass):
    def __init__(self, args)
        # 兩種方式呼叫父類別建構元
        super().__init__(args)
        SuperClass.__init__(self, args)
        
# Multiple Inheritance
class SubClass(Class1, Class2, ...):
    pass
```


### Polymorphism

### Opeartor Overloading
![](http://1.bp.blogspot.com/-FnIuZ0OPYk8/VcJSm4w254I/AAAAAAAAIpA/Y6_3b2r53TA/s1600/operator_overloading.PNG)
- `int()    <=> __int__(self)`
- `float()  <=> __float__(self)`
- `str()    <=> __str__(self)`
- `[index]  <=> __getitem__(self, index)`
- `in       <=> __contains__(self, other)`
- `len      <=> __len__(self)`

```python=
# 剛好以前有作業有用到
# Inheritance、Operator Overloading、Override
# 暫且拿來當例子ㄅ
# 題目為用現成的PriorityQueue實作Reverse
import queue

# 該Class用來封裝資料，目的是要使用自定義的比大小方式
# 藉此達到Reverse
class Wrapper():
    def __init__(self, data):
        self.data = data
    getValue = lambda self : self.data
    # >
    def __ge__(self, other):
        if isinstance(other, Wrapper):
            return not self.data >= other.data
        else:
            raise TypeError
    # >=
    def __gt__(self, other):
        if isinstance(other, Wrapper):
            return not self.data > other.data
        else:
            raise TypeError
    # <
    def __le__(self, other):
        if isinstance(other, Wrapper):
            return not self.data <= other.data
        else:
            raise TypeError
    # <=
    def __lt__(self, other):
        if isinstance(other, Wrapper):
            return not self.data < other.data
        else:
            raise TypeError
    # ==
    def __eq__(self, other):
        if isinstance(other, Wrapper):
            return self.data == other.data
        else:
            raise TypeError

# 該class繼承了現有PriorityQueue
# 並對put、get兩個function做改寫
# 將送進去儲存的資料用上面的class包裝起來
class MyPriorityQueue(queue.PriorityQueue):
    def put(self, data):
        super().put(Wrapper(data))
    def get(self):
        return super().get().getValue()

# 測試函式
if __name__ == '__main__':
    pq = MyPriorityQueue()
    aList = [5,6,73,1,42,3,9,75]
    
    for e in aList:
        pq.put(e)
    
    while True:
        if not pq.empty():
            print(pq.get(), end=' ')
        else:
            break
```

## String

### String Function
> Cause string is immutable object, any function changing the string always return a new string object.
#### 1. Test Function
- `isalnum()`
    - Return True, if string only contains number or alphabet
- `isalpha()`
    - Return True, if string only contains alphabet
- `isdigit()`
    - Return True, if string only contains number
- `isspace()`
    - Return True, if string only contains whitespace charactor
- `isidentifier()`
    - Return True, if string is python identifier
- `islower()`
    - Return True, if string is lower case
- `isupper()`
    - Return True, if string is upper case
- `startswith(s)`
    - Return True, if string starts with `'s'`
- `endswith(s)`
    - Return True, if string ends with `'s'`

#### 2. Search Function
- `find(s)`
    - Return the first found index of `'s'` in the string which starts from index 0
    - Return -1 if `'s'` is not in stirng
- `rfind(s)`
    - Return the first found index of `'s'` in the string which starts from last index
    - Return -1 if `'s'` is not in stirng
- `count(s)`
    - Return how many times `'s'` appers in the string

#### 3. Common Function
- `lower()`
    - Return string is lower case
- `upper()`
    - Return string is upper case
- `replace(old, new)`
    - Return string has replaced old sub_string to new sub_string
    - EX:`str.replace('\n', '').replace('\r', '').replace('\t', '')`
- `split(sep=None, maxsplit=-1)`
    - Return a list of the words in the string, using sep as the delimiter string.
    - EX:`'He ll o'.split(' ')  -->  ['He', 'll', 'o']`
- `join(iterable)`
    - Return a new string that inserted string between the each element in the iterable
    - EX:`','.join(['He', 'll', 'o'])  -->  'He,ll,o'`
- `string.strip()`
    - Return a new string that striped the blank or string you assigned
```python=
string = """    geeks for geeks    """ 

# prints the string by removing leading and trailing whitespaces 
print(string.strip()) 
  
# prints the string by removing geeks 
print(string.strip(' geeks')) 
```

#### 4. Format Function
> 不熟，待補充
兩個常用形式:
```python=
name, age = 'Tom', 8

str1 = "I'm {}, {} years old.".format(name, age)

str1 = "I'm {1}, {0} years old.".format(name, age)# output = I'm 8, Tom years

str1 = "I'm {:^8}, {:*>3} years old.".format(name, age) 
'''
^ 置中； >, 靠右； <, 靠左
'''

str2 = f'I\'m {name}, {age} years old.'

str3 = "%s is %d years old." % (name, age)
```

## Exception Handling
### Basic Type
```python=
# 觸發例外
raise <ExceptionType>
# 處裡例外
try:
    <body>
except <ExceptionType1>:
    <handler1>
...
except <ExceptionTypeN>:
    <handlerN>
except:
    <handlerExcept>
    # 當前面的except都無法抓到合適的型態時, 執行此區塊
else
    <process_else>
    # 當try裡面沒有例外發生時, 執行此區塊
finally:
    <process_finally>
    # 定義一些收拾行為, 不管有沒有發生例外, 最後都會執行此區塊
```
## Inbuilt Data Structure
### List

> List is a mutable object.

> List is like one dimension array, more like vector in C/C++

#### 1. List Functions
- `list.append(elem)`
    - Append elem to the tail of list
- `list.extend(iterable)`
    - Extend the iterable and append all elems of it to the list
    - EX: `[1,2].extend( (4,5) ) --> [1,2,4,5]`
- `list.insert(idx, elem)`
    - Insert the elem to the idx
    - EX: `[1,3,4,5].insert(1,2) --> [1,2,3,4,5]`
- `list.remove(elem)`
    - Remove the **first** elem in the list
    - Raise ValueError if elem is not in the list
    - EX: `[1,2,1].remove(1) --> [2,1]`
- `list.pop([idx])`
    - Remove the idx-th elem
    - Remove the last elem if no idx
- `list.clear()`
    - Clear all elems from the list
    - Same as `del list[:]`
- `list.index(elem, [start [, end]])`
    - Return the idx of the **first** elem
    - Raise ValueError if elem is not in the list
- `list.sort(key=None, reverse=False)`
    - Sort the list by key
    - In-place Sort
- `list.copy()`
    - Return the copied list

```python=
# Sort Example

# Initialize var
a = [9, 4, 7, 1]
a.sort()
## a = [1,4,7,9]

# Initialize var
b = [ (1, 'FC'), (-9, 'XY'), (4, 'KC'), (11, 'BZ') ]

# sort by default
# Try & Test & Think, What is default
b.sort()
## b = [(-9, 'XY'), (1, 'FC'), (4, 'KC'), (11, 'BZ')]

# sorted by 'word'
b.sort(key=lambda x : x[1])
## b = [(11, 'BZ'), (1, 'FC'), (4, 'KC'), (-9, 'XY')]

# sorted by the second character of the 'word'
b.sort(key=lambda x : x[1][1])
## b = [(1, 'FC'), (4, 'KC'), (-9, 'XY'), (11, 'BZ')] 
```

```python=
# Four ways of copy

# Initilize var
a = [1,2,3]

# Copy
# Bad Way
b = []
for x in a:
    b.append(x) or b.insert(len(b), x)
# Better Way
b = a.copy()        # Use list copy function
b = [] + a          # Use list concatenation
b = [x for x in a]  # Use list comprehension


# Try & Test & Think
# Why don't we simply use
b = a
```
#### 2. List Slicing
> List 可以一次取出不只一個元素，用的就是 Slicing

##### Syntax
```python=
list[index1 : index2] #從index1取到index2(不包含index2)
```

##### Example
```python=
x = [1, 2, 3, 4]
x[0:2]  # [1, 2]
x[1:-1] # [2, 3], 1和倒數第一個之間的元素
x[2:0]  # []    , 若第一個索引的位置在第二個索引的位置之前
x[ :2]  # [1, 2], 從開頭開始切
x[2: ]  # [3, 4], 從2開始切到尾
x[ : ]  # [1,2,3,4], 這邊是不一樣的list只是切出所有元素
```




#### 3. List Comprehension
> 這招必學，學會程式碼可以非常簡短!!

##### Basic Type
```python=
list_var = [ x for x in iterable ]

a = [1, 3, 5]
b = [x for x in a]
## b = [1, 3, 5]

apple = [c for c in 'apple']
## apple = ['a', 'p', 'p', 'l', 'e']
```

##### With Condition
```python=
odd_num = [n**2 for n in range(10) if n % 2]
## odd_num = [1, 9, 25, 49, 81]

# Equal to
odd_num = []
for n in range(10):
    if n % 2:
        odd_num.append(n**2)

# Nested If
nums = [n for n in range(100) if n % 2 == 0 if n % 5 == 0]
## nums = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90]

# Equal to
nums = []
for n in range(100):
    if n % 2 == 0:
        if n % 5 == 0:
            nums.append(n)
```

##### With Nested Loop
```python=
nums = [ x*y for x in [1,2,3] for y in [10,20,30] ]
## nums = [10, 20, 30, 20, 40, 60, 30, 60, 90] 

# Equal to what?
# Try & Test
```

#### 4. List Common Usage
```python=
# 1. Transform input to int
nums = list(map(int, input('Input Numbers: ').split(' ')))

# 2. Access elems dirtectly, not access by index
for elem in aList:
    pass

# 3. Get list size
size = len(aList)

# 4. Delete element in list
aList = ['a', 'b', 'c', 3, 4, 5]
del aList[1]     # aList == ['a', 'c', 3, 4, 5]
del aList[ :2]   # aList == [3, 4, 5]
```

#### 5. List Common Error
```python=
aList = list(1, 2, 3)
## Raise TypeError

# Create a list just simply use
aList = [1, 2, 3]

# list(iterable) correct usage
aList = list( (1, 2, 3) )
aList = list( range(1,4) )
```

### Tuple
> 1. Tuple is a immutable object
> 2. Common usage is to store value
> 3. Can't insert value to the tuple and remove value from the tuple

### Dictionary
> 1. 使用key-value的方式儲存資料
> 2. 如同C++ hash map
> 3. key-value可以為任意型別

#### 1. Dictionary Function
- `dict(mapping)`:
    - Return the dict of mapping
    - EX:`dict([('one', 1) ,('two', 2)]) -->  {'one': 1, 'two': 2}`
- `dict(**kwargs)`:
    - EX:`dict(k1='A', k2='B')  -->  {'k1': 'A', 'k2': 'B'}`
- `[]`:
    - Add the value to the key
    - EX: `dict_obj[key] = value`
    - Get the value of the key
    - EX: `var = dict_obj[key]`
    - If no key, raise KeyError
- `dict.update(**kwargs or dict_obj)`
    - Add the value to the original dict_obj
- `dict.clear()`
    - Remove all elems in the dict
- `dict.copy()`
    - Return the copy of the dict
 - `dict.get(key, default=None)`
     - Return the value of the key
     - If no key, return default
     - EX:`dict.get(fun, lambda:print('no fun'))()`
 - `dict.keys()`
     - Return a list of the key in dict
 - `dict.values()`
     - Return a list of the value in dict
 - `dict.items()`
     - Return a list of the key-value pair in dict
 - `dict.pop(key, [default])`
     - Return the value of the key, and pop the key from the dict
     - If key isn't exist, return default
     - If no default , raise KeyError
 - `dict.__contain__(key)`
     - Return True if key in the dict
     - EX:`True if key in dict else False`

#### 2. Dictionary Example
```python=
# dict example with sorted and lambda
dic = {'Mary' : 50, 'Bob': 30, 'Cindy' : 80, 'John' : 100, 'Can' : 10}

# Sort by key
sort_by_key = sorted(dic.items(), key = lambda x:x[0])
## sort_by_key =
## [('Bob', 30), ('Can', 10), ('Cindy', 80), ('John', 100), ('Mary', 50)]

# Sort by value
sort_by_val = sorted(dic.items(), key = lambda x:x[1])
## sort_by_val =
## [('Can', 10), ('Bob', 30), ('Mary', 50), ('Cindy', 80), ('John', 100)]


# Sort with the second word of name and reverse
sort_ch = sorted(dic.items(), key = lambda x:x[0][1], reverse = True)
## sort_ch =
## [('Bob', 30), ('John', 100), ('Cindy', 80), ('Mary', 50), ('Can', 10)]
```

## Module -- JSON
> Library: json

常用函式:
1. `dump(obj, fp)`
    - Python物件轉換成JSON格式字串，並寫入檔案
2. `loda(fp)`
    - 從檔案讀取JSON，並轉換成Python物件
3. `dumps(obj)`
    - 注意s，將Python物件轉換成JSON格式字串
    - 選用參數(有很多，僅列出我用過的)
        - skipkeys
            - default = False
            - True，略過非Python基本型態的key值，(Python基本型態：str、int、float、bool、None)
            - False 遇到非Python基本型態的key值，產生TypeError
        - sort_keys
            - default = False
            - 按照資料key值排序
        - separators
            - default = None
            - If None 預設字典顯示，為`( ' , ' , ' : ' )`，代表每對pair之間用逗點隔開，key與value之間用冒號隔開
            - EX：`json.dumps( { 1 : 'Mary', 2: 'Jack' },  separators=(" | ", " = ") )`
                - Output：`'{"1" = "Mary" | "2" = "Jack"}'`
    4. `loads(str)`
        - 注意s, 將JSON字串轉換成Python物件

型別對照表

| Python |  JSON  |
|:------:|:------:|
|  dict  | object |
|  str   | string |
|  int   | number(int) |
|  float | number(real) |
|  True  | true   |
|  Flase | flase  |
|  None  |  null  |

使用範例
```python=
# 進入Python Interpreter
import json

# 基本型態
a, b, c, d, e, f = 'Hi', 10, 1.0, True, False, None
# dumps function
A = json.dumps(a)
B = json.dumps(b)
C = json.dumps(c)
D = json.dumps(d)
E = json.dumps(e)
F = json.dumps(f)
# type A ~ F，觀察結果

# loads funcion
a1 = json.loads(A)
b1 = json.loads(B)
c1 = json.loads(C)
d1 = json.loads(D)
e1 = json.loads(E)
f1 = json.loads(F) # 用print才看得到
# type a1 ~ f1，觀察結果
# 可以測試一下，a == a1 ~ f == f1，結果都會是True
```
:::info
小結: 基本型態的轉換可以放心使用，沒有問題
:::
```python=
# 進入Python Interpreter
import json

# 資料結構 Tuple List Dictionary
aTuple = ('Tuple', 'will', 'change', 'toList', 99)
aList  = ['Int', 10, 'Float', 1.0, 'String', 'hello', 'LofL', [1,2], (3,4)]
aDict  = {9 : 99, 1.0 : 5.5, 'str' : 'string', None : None}
# dumps function
TD = json.dumps(aTuple)
LD = json.dumps(aList)     
DD = json.dumps(aDict)
# type LD TD DD，觀察結果

# loads function
bTuple = json.loads(TD)
bList  = json.loads(LD)
bDict  = json.loads(DD)
# type (a、b) pair 比較結果
# 可以測試一下，每對pair的比較，結果都會是Flase
```
:::info
小結: 資料結構的轉換必須要注意一些事情
1. List 在轉換時只要其元素都為基本型態，是沒問題的
2. JSON 沒有Tuple的型態，任何Tuple都會被轉成List
3. Dictionary 的key在JSON裡面必須是字串型態，value不受影響
:::

如果一定要非字串型別當作dict key值的話，可以使用二次dump的技巧
```python=
def transKey(dict_data, encode=True):
    tmp_dict = {}
    for key, value in dict_data.items():
        if encode:
            tmp_dict[json.dumps(key)] = value
        else:
            tmp_dict[json.loads(key)] = value
    return tmp_dict

# 使用上
aDict          =  {9 : 99, 1.0 : 5.5, None : None}
# 先進行key轉換，在做dumps
aDict_transKey =  transKey(aDict, encode=True)
jDict_dump     =  json.dumps(aDict_transKey)
# 先做loads，再將key轉回去
jDict_load     =  json.loads(jDict_dump)
bDict          =  transKey(jDict_load, encode=False)

if aDict == bDict:
    print('Successed!!')
```
Json 和檔案相關的簡單基本操作
```python=
# Json & File

aDict = {'1' : 'one', '2' : 'two', '6' : 'six'}
# 寫入檔案(法一)
with open('example.json', 'w') as fp:
    fp.write(json.dumps(aDict))
# 寫入檔案(法二)
json.dump(aDict, open('example.json', 'w'))

# 讀出檔案(法一)
with open('example.json', 'r') as fp:
    data = json.loads(fp.read())
# 讀出檔案(法二)
data = json.load(open('example.json', 'r'))
```

## Third Party Module

```python=
# Download Module
pip install <module name>
# Update Module
pip install -U <module name>
```

```python=
# 下載過的模組
pyperclip  # 剪貼簿(複製、貼上)
openpyxl   # Excel 相關操作
```

### Module -- Pyperclip
> library pyerclip

- copy(*string*)
    - 傳送字串到剪貼簿
- paste()
    - 從剪貼簿接收字串

### Module -- Openpyxl
> library openpyxl

> 用來處理Excel檔案

#### Excel
- Excel 試算表文件 == 活頁簿(workbook)
    - 儲存在.xlsx檔案中
- 活頁簿可有多個工作表(sheet)
    - 正在使用的工作表稱為 active sheet
- 欄從A開始
- 列從1開始

#### Attr & Fun

##### openpyxl
- load_workbook(*file*)
    - file 為 Excel 檔名
    - Return: Workbook Object
```python=
wb = openpyxl.load_workbook('examply.xlsx')
```
- Workbook()
    - 建立空的活頁簿
    - Return: Workbook Object

##### openpyxl.utils
- get_column_letter
    - 將數字轉成欄位代碼
```python=
openpyxl.utils.get_column_letter(27) #AA
```
- column_index_from_string
    - 將欄位代碼轉成數字
```python=
openpyxl.utils.column_index_from_string('AA') # 27
```

##### openpyxl.styles
- Font(*name, size, bold, italic*)
    - 可以自訂儲存格字型別樣式
    - name : 字型名稱, `string`
    - size : 字型大小, `int`
    - bold : 粗體, `bool`
    - italic : 斜體, `bool`
```python=
from openpyxl.styles import Font
f1 = Font(name='Console', size=12, bold=true, italic=true)
<cell>.font = f1
```

##### workbook
- active
    - 取得 active sheet
    - Return: Worksheet Object
- worksheets
    - 取得所有 sheets
    - Return: List
- encoding
    - 取得編碼
    - Return: String
- get_sheet_by_name(*sheet name*)
    - 取得指定 sheet
    - Return: Worksheet Object
- get_sheet_names()
    - 回傳所有的sheet
    - Return: List
- create_sheet(*index=, title=*)
    - 建立分頁，預設加在最後，可以指定index
- remove_sheet(*Sheet Objet*)
    - 刪除指定的分頁
- save(*excel name*)
    - 儲存成Excel檔

    
##### Worksheet
- title
    - 取得 sheet name
- max_row
    - 取得工作表列的最大值
- max_column
    - 取得工作表行的最大值
- freeze_panes
    - 設定凍結窗格(不管捲到哪，都會顯示在最上層)
    - 需要注意的是，凍結的範圍是**小於給定的值**
        - A1 -> 沒有凍結
            - 等同於`None`
        - B2 -> 凍結欄A、列1
        - C1 -> 凍結欄A、欄B
```python=
ws.freeze_panes = 'A2'
```
- row_dimensions[*row*].[height | width]
    - 設定指定列的高度或寬度
```python=
ws.row_dimensions[1].height = 50
```
- column_dimensions[*row*].[height | width]
    - 設定指定行的高度或寬度
```python=
ws.column_dimensions['B'].width = 15
```
- cell(*row=i, column=j*)
    - 取得i列j行的儲存格
    - Return: Cell Object
```python=
# 另外一種取法
sheet['A1'].value
```
- merge_cells(*range*)
    - 合併儲存格
```python=
ws.merge_cells('A1:D3')  # merge 12 cells
```
- unmerge_cells(*range*)
    - 取消合併儲存格
```python=
ws.unmerge_cells('A1:D3')  # merge 12 cells
```

##### cell
- value
    - 取得值或修改值
- row
    - 取得該cell位於哪一列
- column
    - 取得該cell位於哪一行
- coordination
    - 取得該cell位置(EX: A1、B2)

##### Sheet Slice
```python=
import openpyxl

wb = openpyxl.load_workbook('example.xlsx')
st = wb.get_sheet_by_name('Sheet1')

for row in sheet['A1':'C3']:
    for cell in row:
        print(cell.coordinate, cell.value)
```

##### Formula
> Excel中公式是由=開頭
```python=
import openpyxl
wb = openpyxl.Workbook()
ws = wb.actice
ws['A1'], ws['A2'] = 100, 200
ws['A3'] = '=SUM(A1:A2)'
wb.save('formula.xslx')
```

##### Example
###### Chart
```python=
import openpyxl
wb = openpyxl.Workbook()
ws = wb.active
# Create simple data in cell
for i in range(1,11):
    ws['A'+str(i)] = i
# Create Reference Object
ref = openpyxl.chart.Reference(ws, min_col=1, min_row=1, max_col=1, max_row=10)
# Create Series Object
series = openxl.chart.Series(ref, title='First series')

chart = openpyxl.chart.BarChart()  # 直方圖
chart.title = '直方圖'
chart.append(series)  # 將序列資料加到圖表中
ws.add_chart(chart, 'C5')  # 將圖表加到分頁中指定的位置
wb.save('sammpleChart.xlsx')
```
:::danger
雖然openpyxl可以製作圖表，但在讀取Excel時，無法讀取圖表
:::
- 其他可畫的圖表
    - openpyxl.chart.LineChart(), 折線圖
    - openpyxl.chart.ScatterChart(), 散佈圖
    - openpyxl.chart.PieChart(), 圓餅圖