# 9. 模組與套件

## I. 何謂模組(Module)?

就是一個含有多個「函式」、「變數」、「類別」的Python檔案，通常隸屬於某套件之下

## II. 何謂套件(Package)? <a href="#he-wei-tao-jian-package" id="he-wei-tao-jian-package"></a>

在Python中，套件可視為模組的目錄，一個套件中可有多個模組

下方為專案、套件、子套件、模組間的關係例圖

```
專案名稱
 └── 套件目錄
      ├── __init__.py
      ├── 子套件目錄1
      │    ├── __init__.py
      │    ├── moduleX.py
      │    └── moduleY.py
      ├── 子套件目錄2
      │    ├── __init__.py
      │    └── moduleZ.py
      └── moduleA.py
```

* Python中，**套件**其實與**函式庫**差不多，分為：
  *   **標準函式庫**(standard library)

      > 安裝 python 時一併安裝的套件【如：and、float、dict...】
  *   **內建函式庫**(built-in library)

      > 安裝 python 時一併安裝的套件【如：math、random、time...】
  *   **外部函式庫**(external library)

      > 需要另外安裝的模組與套件【如：Numpy、Pandas、SciPy...】

## III. 為何需要套件、模組? <a href="#wei-he-xu-yao-tao-jian-mo-zu" id="wei-he-xu-yao-tao-jian-mo-zu"></a>

主要目的是希望讓專案更條理、更組織化，此外還能方便打包分給別人使用

### A. 模組的種類

1. Python內建的模組
2. 自行安裝的模組
3. 同個專案（資料夾）的Python檔案(.py)

> 匯入後，模組名稱為成為一個**變數**

### B. 模組的使用 <a href="#shi-yong-python-nei-jian-mo-zu" id="shi-yong-python-nei-jian-mo-zu"></a>

**方法一**

利用`import`將**模組(Module)**&#x532F;入到此程式中：`import [module]`

```python
import random
print(random.randint(0, 10)) # 使用random模組下的randint()物件
```

**方法二**

如果只希望匯入模組中的特定物件則可以使用：`from [module] import [objects]`&#x20;

```python
from random import randint
print(randint(0, 10)) # 使用randint()物件時，寫法會比較簡便
```

```python
from random import randint, choice, sample
print(randint(0, 10)) # 3
print(choice([1, 2, 3, 4, 5])) # 5
print(sample(range(100), k=3)) # [38, 57, 8]
```

**方法三**

匯入模組，但這個名字可能跟其他名稱衝突(或為方便)，因而**改名**：`import [module] as [module new_name]`

```python
import random as rd
print(rd.randint(0, 10))
```

**方法四**

若希望匯入整個模組可以使用`*`【此方式較不推薦】：`from [module] import *`

```python
from random import *
print(randint(0, 10))
```

> 不推薦原因是容易造成名稱衝突

### C. 使用Python內建模組 <a href="#shi-yong-python-nei-jian-mo-zu" id="shi-yong-python-nei-jian-mo-zu"></a>

```python
import math # 載入math模組

# math模組內的變數pi
print(math.pi) # 3.141592653589793
# math模組內的變數e
print(math.e) # 2.718281828459045

# math模組內的函式sqrt()
print(math.sqrt(2)) # 1.4142135623730951
# math模組內的函式sin()及radians()
print(math.sin(math.radians(90))) # 1.0
```

```python
import time
print(time) # <module 'time' (built-in)>
print(time.time()) # 1597855263.0922987 (時間戳記，可用來測程式執行效率)
print(time.ctime()) # Thu Aug 20 00:41:03 2020
time.sleep(5)
print(time.ctime()) # Thu Aug 20 00:41:08 2020
```

```python
import random # 載入random模組
print(random) # <module 'random' from 'C:\\Program Files\\Python38\\lib\\random.py'>
print(random.random()) # 0.750220339369926
print(random.uniform(0,2)) # 0~2之間隨機小數
print(random.randint(0, 10)) # 9

print(random.choice("hello")) # e
print(random.choice(["a", "b", "c"])) # c

# 不允許重複隨機
print(random.sample(["a", "b", "c"] , 2)) # ['c', 'a']

# 允許重複隨機
print([random.randint(1,6) for _ in range(3)]) # [5, 3, 5]
```

```python
import datetime
print(datetime) # <module 'datetime' from 'C:\\Program Files\\Python38\\lib\\datetime.py'>
print(datetime.datetime.now()) # 2020-08-20 00:36:50.681570
t = datetime.datetime(2019, 1, 2,  3, 4, 5, 6)
print(t) # 2019-01-02 03:04:05.000006
print(t.year, t.month, t.day) # 2019 1 2
print(t.hour, t.minute, t.second, t.microsecond) # 3 4 5 6
print(t - datetime.datetime.now()) # -596 days, 2:27:14.318436
print((t - datetime.datetime.now()).days) # -596
```

### D. 查看模組內函式

* 使用`dir()`函式
  * `dir()`若不帶任何參數物件，它的執行結果會顯示出當前範圍內的變數、方法和屬性
  * `dir()`若帶參數時，返回參數的屬性、方法

```python
print(dir()) # ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']

import time
x = dir(time)
print(x) # ['_STRUCT_TM_ITEMS', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'altzone
```

### E. 自己創建模組 <a href="#chuang-jian-mo-zu" id="chuang-jian-mo-zu"></a>

先假設我們的專案結構長成下面這個樣子

```
firstPythonProject
 └── packageAB
      ├── __init__.py
      ├── packageXY
      │    ├── __init__.py
      │    ├── moduleX.py
      │    └── moduleY.py
      ├── moduleA.py
      └── main.py
```

> 套件內一定要有`__init__.py`檔，用來宣稱自己是一個套件。即使它內容是空的也沒關係

假設模組`moduleA.py`裡頭定義了一個函式： `hello_func()`

```python
def hello_func():
    print('Hello Python!')
```

那麼在同樣目錄下的模組`main.py`想要重複使用`hello_func`這個函式，就能夠藉由`import`從模組`moduleA.py`中匯入

```python
import moduleA

moduleA.hello_func() # Hello!
```

```python
from moduleA import hello_func

hello_func() # Hello
```

### F. 須創建模組的情況

你可能會有疑問，**函式**就能夠簡化類似步驟的程式碼，為何還需要**模組**？這樣反而要透過模組再引入想要的**函式**\


其實是因為如果將許多函式與主程式都寫在同一個檔案中，會讓整個檔案的程式碼顯得非常多、不易閱讀，最重要的是**不易維護**或**供其他程式使用**\


所以通常我們習慣將類似領域的函式獨立出來成為一個模組\


例如：將用在數學方面的「函式」及「變數」都獨立出來包成一個`math.py`的模組

```python
"""
math.py module
"""

def max(a, b):
    return a if a > b else b
def min(a, b):
    return a if a < b else b

def sum(numbers):
    total = 0
    for number in numbers:
        total += int(number)
    return total

pi = 3.141592653589793
e = 2.718281828459045
```

<mark style="color:green;">【換你試試看1】</mark>

> 遊戲規則：電腦要可以隨機出一個數字，讓使用者猜。另外，每次猜到數字時，要顯示這次花幾秒才猜到答案\
> 提示：可利用`time.time()`的相減完成\
> ![](https://i.imgur.com/E3CEriU.png)

```python
import random
import time

ans = random.randint(1, 1001)

count = 0


while 1:
    i = int(input("請猜一個1000內的正整數："))

    count += 1
    if i > ans:
        print("答案小於您的猜測")
    elif i < ans:
        print("答案大於您的猜測")
    else:
        print("恭喜您猜對了")
        print("您一共用了",count,"次 猜對了答案")
        break
```

<mark style="color:green;">【換你試試看2】</mark>

> 製作出一個猜拳遊戲。額外規則：玩家需要在5秒內出拳，不然電腦會"森77"，除此之外，電腦會隨機出拳，不能當哆啦A夢

## IV. 附檔

### A. 程式碼提示

{% file src="https://1394144544-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Ma3iO86k0kEA04rPhPo%2Fuploads%2FERJ4ZGsHPntZKc93sFzX%2F%E6%8C%91%E6%88%B01%E6%8F%90%E7%A4%BA%E7%A2%BC.txt?alt=media&token=bdb0305c-b6c2-44f3-94f3-541503743c77" %}

{% file src="https://1394144544-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Ma3iO86k0kEA04rPhPo%2Fuploads%2FUXD58bf3Rsowpe5FdSOw%2F%E6%8C%91%E6%88%B02%E6%8F%90%E7%A4%BA%E7%A2%BC.txt?alt=media&token=e6f51b7f-3361-4117-80db-9edee8c2ab13" %}

{% file src="https://1394144544-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Ma3iO86k0kEA04rPhPo%2Fuploads%2F8UnJR3UyPB5QnZ7KwICE%2F%E6%8C%91%E6%88%B04%E6%8F%90%E7%A4%BA%E7%A2%BC.txt?alt=media&token=2bc20cec-a6a8-4e54-8bfa-02aab4c57b00" %}
