โมดูลและแพ็กเกจของ Python: import, pip, venv และไลบรารีอธิบายง่าย ๆ
โมดูลใน Python คือไฟล์ .py แพ็กเกจคือโฟลเดอร์ที่รวมโมดูลไว้ และไลบรารีคือชุดของแพ็กเกจที่เรียกใช้ด้วยคำสั่ง import ไลบรารีจากภายนอกติดตั้งด้วย pip หรือ uv ลงในสภาพแวดล้อมเสมือน venv ซึ่งเป็นโฟลเดอร์แยกที่มีตัวแปลภาษาและแพ็กเกจของโปรเจกต์นั้น ทำให้เวอร์ชันของแต่ละโปรเจกต์ไม่ตีกัน
โมดูลใน Python คือไฟล์ที่มีโค้ด และ import คือวิธีนำฟังก์ชันและคลาสจากไฟล์นั้นมาใช้ โมดูลรวมกันเป็นแพ็กเกจ แพ็กเกจรวมกันเป็นไลบรารี และเพื่อไม่ให้ไลบรารีของแต่ละโปรเจกต์ตีกัน เราติดตั้งไลบรารีภายนอกลงในสภาพแวดล้อมเสมือน venv บทความนี้จะอธิบายทั้งกระบวนการ ตั้งแต่สิ่งที่เกิดขึ้นตอน import โครงสร้างแพ็กเกจ การใช้ pip และ venv ไปจนถึง lazy import ที่มาใน Python 3.15
โมดูล แพ็กเกจ ไลบรารี ต่างกันอย่างไร
| คำ | คืออะไร | ตัวอย่าง |
|---|---|---|
| โมดูล (module) | ไฟล์ .py หนึ่งไฟล์ ชื่อโมดูลคือชื่อไฟล์ที่ไม่มีนามสกุล | categories.py → import categories |
| แพ็กเกจ (package) | โฟลเดอร์ที่มีโมดูลและไฟล์ __init__.py | expenses/report.py → from expenses.report import build |
| ไลบรารี (library) | ชุดโมดูลและแพ็กเกจสำเร็จรูปสำหรับงานอย่างหนึ่ง | requests, pandas, pathlib ในไลบรารีมาตรฐาน |
| ดิสทริบิวชัน (แพ็กเกจบน PyPI) | ไฟล์บีบอัดที่ pip ใช้ติดตั้ง | pip install requests |
ในการพูดคุยทั่วไปมักใช้คำว่า "ไลบรารี" "แพ็กเกจ" และ "โมดูล" สลับกันซึ่งเป็นเรื่องปกติ สิ่งสำคัญคือ ในทางเทคนิค import จะโหลดโมดูลหรือแพ็กเกจเสมอ ส่วน pip ติดตั้งดิสทริบิวชันจาก PyPI ซึ่งภายในมีแพ็กเกจอยู่
import ใน Python ทำงานอย่างไร
การ import มีหลายรูปแบบ และทุกแบบพบได้ในโค้ดจริง:
import math # ทั้งโมดูล เรียกผ่าน math.sqrt
from math import sqrt, pi # เฉพาะชื่อที่ต้องการ
import datetime as dt # ตั้งชื่อย่อ
from collections import Counter as C
print(math.sqrt(16), sqrt(9), pi)
print(dt.date.today())
ไลบรารีขนาดใหญ่นิยมตั้งชื่อย่อ เช่น import numpy as np และ import pandas as pd ส่วนการใช้ from module import * ไม่แนะนำ เพราะทำให้เนมสเปซรกและไม่รู้ว่าชื่อนั้นมาจากไหน
import ทำอะไรจริง ๆ
- ค้นหาโมดูลในแคช
sys.modulesถ้าโหลดไว้แล้วก็คืนค่าโมดูลนั้นทันที - หากไม่มี ค้นหาไฟล์ตามรายการโฟลเดอร์ใน
sys.path: โฟลเดอร์ของสคริปต์ที่รัน ไลบรารีมาตรฐาน และ site-packages ของสภาพแวดล้อม - รันไฟล์ที่พบทั้งไฟล์ตั้งแต่บนลงล่าง
- เก็บออบเจกต์โมดูลไว้ในแคชและผูกกับชื่อ
จากขั้นตอนนี้มีข้อสรุปเชิงปฏิบัติสองข้อ การ import โมดูลเดิมซ้ำแทบไม่เสียเวลา เพราะโค้ดไม่ถูกรันรอบที่สอง และโค้ดระดับบนสุดของโมดูลทั้งหมด รวมถึง print และการคำนวณหนัก จะทำงานทันทีที่ถูก import
import sys
for p in sys.path:
print(p)
print("math" in sys.modules)
if __name__ == "__main__"
เนื่องจากการ import รันไฟล์ จึงต้องมีวิธีแยกระหว่าง "ถูกรันเป็นสคริปต์" กับ "ถูก import" ตัวแปร __name__ ทำหน้าที่นี้ เมื่อรันไฟล์โดยตรงค่าจะเป็น "__main__" และเมื่อถูก import ค่าจะเป็นชื่อโมดูล
# categories.py
def guess(title: str) -> str:
return "อาหาร" if "คาเฟ่" in title.lower() else "อื่น ๆ"
if __name__ == "__main__":
print(guess("คาเฟ่ใกล้บ้าน")) # จะทำงานเฉพาะเมื่อรัน python categories.py
สำหรับแพ็กเกจมีอีกทางเลือกคือใส่ไฟล์ __main__.py ไว้ในแพ็กเกจ แล้วรันด้วย python -m expenses
โมดูลมาตรฐานของ Python
Python มาพร้อมกับ "แบตเตอรี่" คือโมดูลในตัวหลายร้อยตัวที่ใช้ได้ทันทีโดยไม่ต้องติดตั้ง ตัวที่ใช้บ่อยได้แก่:
| โมดูล | ใช้ทำอะไร |
|---|---|
os, sys, pathlib, shutil | ระบบ พาธ และไฟล์ |
json, csv, pickle | รูปแบบข้อมูล |
datetime, time, calendar | วันที่และเวลา |
math, random, statistics, fractions | ตัวเลข |
re, string | การจัดการข้อความ |
collections, itertools, functools | โครงสร้างข้อมูลและตัววนซ้ำ |
logging, argparse, subprocess | บันทึกล็อก อาร์กิวเมนต์บรรทัดคำสั่ง และรันโปรแกรมอื่น |
asyncio, threading | อะซิงโครนัสและเธรด |
รายชื่อโมดูลในไลบรารีมาตรฐานทั้งหมดเก็บไว้ใน sys.stdlib_module_names (Python 3.10 ขึ้นไป) และดูว่าโมดูลมีอะไรบ้างได้ด้วย dir(math) และ help(math)
สร้างแพ็กเกจของตัวเอง: โครงสร้างโปรเจกต์
เมื่อโมดูลมีจำนวนมากขึ้น ให้รวบรวมไว้เป็นแพ็กเกจ โครงสร้างที่นิยมคือ src-layout:
expenses-project/
├── pyproject.toml
└── src/
└── expenses/
├── __init__.py
├── cli.py
├── parsing.py
├── categories.py
└── report.py
ภายในแพ็กเกจ โมดูลจะ import กันโดยใช้ชื่อแพ็กเกจ (from expenses.categories import guess) หรือแบบสัมพันธ์ (from .categories import guess) การ import แบบสัมพันธ์ใช้ได้เฉพาะภายในแพ็กเกจ ไม่ใช้ได้กับไฟล์ที่ถูกรันโดยตรงเป็นสคริปต์
pip ใน Python: ติดตั้งไลบรารี
ไลบรารีภายนอกอยู่ในคลัง PyPI และติดตั้งด้วย pip ซึ่งมาพร้อมกับ Python วิธีที่ปลอดภัยที่สุดคือเรียกผ่านตัวแปลภาษาโดยตรง เพื่อให้ไลบรารีถูกติดตั้งลงใน Python ตัวเดียวกับที่คุณใช้รันโค้ด
python -m pip install requests # ติดตั้ง
python -m pip install "pandas>=2.3" # ระบุเวอร์ชันขั้นต่ำ
python -m pip install --upgrade requests
python -m pip uninstall requests # ถอนการติดตั้ง
python -m pip list # รายการแพ็กเกจที่ติดตั้งอยู่
python -m pip show requests # เวอร์ชันและ dependency
python -m pip freeze > requirements.txt
python -m pip install -r requirements.txt
บน Windows มักใช้ตัวเรียก py แทน python หากไม่พบ pip ให้ติดตั้งด้วยโมดูลในตัว: python -m ensurepip --upgrade
ไลบรารี Python ยอดนิยม
แพ็กเกจบน PyPI มีหลายแสนตัว แต่โปรเจกต์ส่วนใหญ่ใช้รายการสั้น ๆ ชุดเดียวกัน ควรเริ่มจากรายการนี้แทนการค้นหาของแปลก ๆ
| งาน | ไลบรารี |
|---|---|
| คำขอ HTTP และ API | requests, httpx |
| วิเคราะห์ข้อมูลและตาราง | pandas, numpy |
| กราฟ | matplotlib |
| เว็บเซอร์วิส | fastapi, django, flask |
| การทดสอบ | pytest |
| รูปภาพ | pillow (import เป็น PIL) |
สังเกตว่าชื่อที่ใช้ติดตั้งกับชื่อที่ใช้ import ไม่จำเป็นต้องตรงกัน เช่น ติดตั้งด้วย pip install pillow แต่เขียน from PIL import Image หรือติดตั้ง opencv-python แล้ว import เป็น cv2 หาก import ล้มเหลวทันทีหลังติดตั้ง ให้ตรวจในเอกสารของแพ็กเกจว่าใช้ชื่อใดในการ import
การติดตั้งไลบรารีบน Linux
บน Debian และ Ubuntu รุ่นใหม่ การรัน pip install ลงใน Python ของระบบจะจบด้วยข้อผิดพลาด externally-managed-environment (PEP 668) นี่คือการป้องกัน เพราะ Python ของระบบเป็นของตัวจัดการแพ็กเกจของ OS และ pip ไม่ควรทำให้มันเสีย วิธีที่ถูกต้องไม่ใช่ --break-system-packages แต่คือการใช้สภาพแวดล้อมเสมือน
venv ใน Python: สภาพแวดล้อมเสมือน
สภาพแวดล้อมเสมือนคือโฟลเดอร์แยกที่มีตัวแปลภาษาของตัวเอง (เป็นลิงก์ไปยัง Python) และ site-packages ของตัวเอง ไลบรารีที่ติดตั้งในนั้นจะมองเห็นได้เฉพาะโปรเจกต์นั้น ดังนั้นโปรเจกต์หนึ่งใช้ Django 4 ได้ ส่วนอีกโปรเจกต์ใช้ Django 5 โดยไม่ตีกัน
โมดูล venv มาพร้อม Python อยู่แล้ว ไม่ต้องติดตั้งแยก (บน Debian/Ubuntu บางครั้งต้องติดตั้งแพ็กเกจระบบ python3-venv ก่อน)
# สร้างสภาพแวดล้อมในโฟลเดอร์ .venv
python -m venv .venv
# เปิดใช้งาน: Linux และ macOS
source .venv/bin/activate
# เปิดใช้งาน: Windows PowerShell
.venv\Scripts\Activate.ps1
# ตอนนี้ pip จะติดตั้งแพ็กเกจลงใน .venv
python -m pip install requests
python -c "import sys; print(sys.prefix)"
# ออกจากสภาพแวดล้อม
deactivate
หลังเปิดใช้งาน หน้าบรรทัดในเทอร์มินัลจะแสดง (.venv) ไม่ควรนำโฟลเดอร์สภาพแวดล้อมเข้า git (เพิ่ม .venv/ ใน .gitignore) และไม่ควรย้ายไปเครื่องอื่น ให้เก็บรายการ dependency ไว้แทนแล้วสร้างสภาพแวดล้อมใหม่ หาก PowerShell ไม่ยอมรันสคริปต์เปิดใช้งาน ให้อนุญาตสำหรับผู้ใช้: Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
uv และ pyproject.toml
โปรเจกต์สมัยใหม่กำหนด dependency ไว้ในไฟล์ pyproject.toml ตัวจัดการ uv ที่ใช้ในคอร์สนี้รวมการสร้างสภาพแวดล้อมและการติดตั้งเข้าด้วยกัน uv add pandas จะดาวน์โหลดแพ็กเกจ เขียนลงใน pyproject.toml และล็อกเวอร์ชันที่แน่นอนไว้ใน uv.lock เพื่อนร่วมทีมที่โคลนโปรเจกต์ลงมาสั่ง uv sync ก็จะได้สภาพแวดล้อมที่เหมือนกันทุกประการ
[project]
name = "expenses"
version = "0.1.0"
requires-python = ">=3.15"
dependencies = [
"pandas>=2.3",
]
[project.scripts]
expenses = "expenses.cli:main"
lazy import ใน Python 3.15 (PEP 810)
ของใหม่หลักใน Python 3.15 ที่เกี่ยวกับหัวข้อนี้คือคีย์เวิร์ด lazy ไลบรารีขนาดใหญ่โหลดนาน จากการทดสอบในคอร์สบนอิมเมจ python:3.15-slim การ import pandas ปกติใช้เวลาราว 415 มิลลิวินาที ส่วน lazy import pandas ใช้เวลาราว 13 มิลลิวินาที สำหรับยูทิลิตีบรรทัดคำสั่งที่รันเพื่อดู --help ความต่างนี้มากมาย
lazy import pandas as pd
lazy from expenses.report import build_report
def main() -> None:
...
df = pd.read_csv(path) # โหลด pandas จริงตรงนี้
แทนที่จะโหลดโมดูลทันที ชื่อจะถูกผูกกับออบเจกต์พร็อกซี (types.LazyImportType) และการ import จริงเกิดขึ้นเมื่อมีการเข้าถึงชื่อนั้นครั้งแรก หากไม่มีการเข้าถึงเลย ก็ไม่เสียเวลาเลย
ข้อจำกัดของ lazy import
- ใช้ได้เฉพาะระดับบนสุดของโมดูล ภายในฟังก์ชัน คลาส หรือ
try/exceptจะเกิดSyntaxError - ห้ามใช้
lazy from x import * ModuleNotFoundErrorจะปรากฏตอนใช้งานครั้งแรก ไม่ใช่ตอนเริ่มโปรแกรม- ผลข้างเคียงของโมดูล (เช่น การลงทะเบียนปลั๊กอิน) จะเลื่อนไปตามเวลาที่โหลดจริง
หากต้องการให้ทุก import เป็น lazy พร้อมกันใช้แฟล็ก -X lazy_imports=all หรือตัวแปรสภาพแวดล้อม PYTHON_LAZY_IMPORTS=all ส่วนการยกเว้นบางโมดูลทำได้ด้วย sys.set_lazy_imports_filter() สำหรับโค้ดที่ต้องรันบนเวอร์ชันเก่าด้วย มีตัวแปรระดับโมดูล __lazy_modules__ = ["pandas"] Python 3.15 จะทำให้โมดูลที่ระบุเป็น lazy ส่วนเวอร์ชันเก่าจะ import ตามปกติ ดูว่าเวลาหายไปตอนเริ่มโปรแกรมที่ไหนได้ด้วย python -X importtime -c "import pandas" ประโยชน์จะเห็นชัดที่สุดในโปรแกรมที่รันบ่อยแต่แต่ละครั้งสั้น ๆ ส่วนเซิร์ฟเวอร์ที่ทำงานต่อเนื่องแทบไม่ได้ประโยชน์
ข้อผิดพลาดที่พบบ่อย
ModuleNotFoundError: No module named 'requests'แสดงว่าไลบรารีถูกติดตั้งลงใน Python อีกตัว หรือยังไม่ได้เปิดใช้งานสภาพแวดล้อม ตรวจด้วยpython -m pip show requestsโดยใช้ตัวแปลภาษาตัวเดียวกัน- ตั้งชื่อไฟล์ของตัวเองว่า
json.pyrandom.pyหรือcsv.pyโฟลเดอร์ของสคริปต์จะอยู่หน้าสุดในsys.pathและบดบังโมดูลมาตรฐาน Python 3.13 ขึ้นไปจะแนะนำให้เปลี่ยนชื่อในข้อความผิดพลาด ให้เปลี่ยนชื่อไฟล์และลบโฟลเดอร์__pycache__ - import วนกลับ (circular import) เช่น
a.pyimportb.pyและb.pyimporta.pyให้แยกส่วนที่ใช้ร่วมกันออกเป็นโมดูลที่สาม หรือ import ไว้ภายในฟังก์ชัน - รัน pip ภายในตัวแปลภาษา Python (
>>> pip install) จะได้SyntaxErrorคำสั่ง pip ต้องรันในเทอร์มินัล
สรุป
โมดูลคือไฟล์ แพ็กเกจคือโฟลเดอร์ของโมดูล และไลบรารีคือชุดที่พร้อมใช้ import จะค้นหาโมดูลในแคชและใน sys.path แล้วรันหนึ่งครั้ง ไลบรารีภายนอกควรติดตั้งด้วย pip หรือ uv ลงในสภาพแวดล้อม venv เท่านั้น เพื่อไม่ให้โปรเจกต์รบกวนกันและทำซ้ำได้ง่าย หัวข้อนี้ถูกสอนแบบลงมือทำในคอร์ส Python โดยโปรแกรมติดตามค่าใช้จ่ายจะถูกแปลงเป็นแพ็กเกจที่มี dependency และเริ่มทำงานได้เร็วขึ้นด้วย lazy import
คำถามที่พบบ่อย
ต้องสร้าง venv สำหรับทุกโปรเจกต์หรือไม่?
ควรทำ เพราะแต่ละโปรเจกต์มีเวอร์ชันไลบรารีของตัวเอง การแยกสภาพแวดล้อมป้องกันความขัดแย้งและไม่ทำให้ Python ของระบบรกไปด้วยแพ็กเกจ
pip ต่างจาก uv อย่างไร?
pip ติดตั้งแพ็กเกจอย่างเดียว ส่วน uv ยังสร้างสภาพแวดล้อม บันทึก dependency ลงใน pyproject.toml และล็อกเวอร์ชันที่แน่นอนในไฟล์ lock โดยเร็วกว่ามาก
จะดูรายการไลบรารี Python ที่ติดตั้งแล้วได้อย่างไร?
รัน python -m pip list ในสภาพแวดล้อมที่ต้องการ ส่วน pip freeze จะแสดงรายการเดียวกันในรูปแบบของ requirements.txt
โมดูลที่ติดตั้งแล้วถูกเก็บไว้ที่ไหน?
ในโฟลเดอร์ site-packages ของสภาพแวดล้อมนั้น ดูพาธของโมดูลได้จากแอตทริบิวต์ __file__ เช่น import requests แล้ว print(requests.__file__)
ย้ายโฟลเดอร์ venv ไปเครื่องอื่นได้หรือไม่?
ไม่ได้ สภาพแวดล้อมผูกกับพาธและตัวแปลภาษาของเครื่องนั้น ให้ย้ายรายการ dependency แทนแล้วสร้างสภาพแวดล้อมใหม่
หัวข้อนี้สอนอย่างละเอียดพร้อมฝึกปฏิบัติในคอร์ส “Python 3.15: от новичка до профи”
ไปที่คอร์ส