# 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. 須創建模組的情況

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

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

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

例如：將用在數學方面的「函式」及「變數」都獨立出來包成一個`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="/files/CseSPoQ1L7Z8l6ehc9Qt" %}

{% file src="/files/Qezg8Z0fjg6OpvKKd4No" %}

{% file src="/files/iMhMmGJH3yGHGlMrLKeu" %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://simplife.gitbook.io/python/xin-shou-cun/9.-mo-zu-yu-tao-jian.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
