พื้นฐาน

โมดูลและแพ็กเกจของ Python: import, pip, venv และไลบรารีอธิบายง่าย ๆ

อัปเดต: · Skillaria.Top

โมดูลใน 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__.pyexpenses/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 ทำอะไรจริง ๆ

  1. ค้นหาโมดูลในแคช sys.modules ถ้าโหลดไว้แล้วก็คืนค่าโมดูลนั้นทันที
  2. หากไม่มี ค้นหาไฟล์ตามรายการโฟลเดอร์ใน sys.path: โฟลเดอร์ของสคริปต์ที่รัน ไลบรารีมาตรฐาน และ site-packages ของสภาพแวดล้อม
  3. รันไฟล์ที่พบทั้งไฟล์ตั้งแต่บนลงล่าง
  4. เก็บออบเจกต์โมดูลไว้ในแคชและผูกกับชื่อ

จากขั้นตอนนี้มีข้อสรุปเชิงปฏิบัติสองข้อ การ 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 และ APIrequests, 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.py random.py หรือ csv.py โฟลเดอร์ของสคริปต์จะอยู่หน้าสุดใน sys.path และบดบังโมดูลมาตรฐาน Python 3.13 ขึ้นไปจะแนะนำให้เปลี่ยนชื่อในข้อความผิดพลาด ให้เปลี่ยนชื่อไฟล์และลบโฟลเดอร์ __pycache__
  • import วนกลับ (circular import) เช่น a.py import b.py และ b.py import a.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: от новичка до профи”

ไปที่คอร์ส