อะไรคือความแตกต่างระหว่างโมดูล Python และแพ็คเกจ Python?


576

อะไรคือความแตกต่างระหว่างโมดูล Python และแพ็คเกจ Python?

ดูเพิ่มเติม: "แพคเกจ" และ "โมดูล" แตกต่างกันอย่างไร (สำหรับภาษาอื่น ๆ )


9
ฉันอาจจะผิด แต่สำหรับฉัน: โมดูลนั้นเป็นไฟล์หลามหนึ่งไฟล์ แพ็คเกจเป็นโฟลเดอร์ที่มีโมดูล (ไฟล์หลาม)
lc2817

36
ในการพิจารณาเป็นแพ็คเกจโฟลเดอร์นั้นจะต้องมี__init__.pyไฟล์
Giulio Piancastelli

@ lc2817: เป็นกรณีที่พบบ่อยที่สุด แต่ไม่จำเป็นสำหรับโมดูลที่จะโหลดจากระบบไฟล์เช่นดูfrom plumbum.cmd import lsการติดตั้ง
jfs


ชุมชนสร้างความแตกต่างระหว่างแพคเกจ Python และแพ็คเกจที่ใช้เพื่อกระจายองค์ประกอบ Python เช่น PyPI / wheels / etc อย่างไร ทั้งสองดูเหมือนแอปพลิเคชันต่าง ๆ ของคำว่า "แพ็คเกจ" สำหรับฉัน
davidA

คำตอบ:


373

โมดูลเป็นไฟล์เดียว (หรือไฟล์) ที่นำเข้าภายใต้การนำเข้าและใช้งานครั้งเดียว เช่น

import my_module

แพคเกจคือชุดของโมดูลในไดเรกทอรีที่ให้ลำดับชั้นของแพคเกจ

from my_package.timing.danger.internets import function_of_love

เอกสารประกอบสำหรับโมดูล

รู้เบื้องต้นเกี่ยวกับแพ็คเกจ


54
เมื่อคุณพูดว่า: "โมดูลเป็นไฟล์เดียว (หรือไฟล์) ที่นำเข้าภายใต้การนำเข้าหนึ่ง" คุณสามารถอธิบายสถานการณ์ที่โมดูลมีมากกว่าหนึ่งไฟล์ได้หรือไม่ หรือฉันกำลังอ่านสิ่งที่คุณหมายถึงผิด?
ผู้ใช้

5
คุณไม่ต้องการไฟล์เพื่อสร้างโมดูลเช่นคุณสามารถนำเข้าโมดูลจากไฟล์ zip เหมือนกันสำหรับแพคเกจ มีเพียงคลาสเดียวสำหรับโมดูล / แพ็คเกจใน Python แพคเกจเป็นเพียงโมดูลที่มี__path__แอตทริบิวต์
jfs

33
แพคเกจเป็นโมดูลเกินไป พวกมันถูกบรรจุไว้ต่างกัน พวกเขาจะเกิดขึ้นจากการรวมกันของไดเรกทอรีบวก__init__.pyไฟล์ พวกเขาเป็นโมดูลที่สามารถมีโมดูลอื่น ๆ
Martijn Pieters

15
@Jacquot แน่ใจว่าดูระบบการนำเข้าในเอกสารอ้างอิง: มันเป็นสิ่งสำคัญที่จะเก็บไว้ในใจว่าทุกแพ็กเกจเป็นโมดูล
Martijn Pieters

6
@Jacquot: และอภิธานศัพท์บน“ แพ็คเกจ” : โมดูล Python ซึ่งสามารถบรรจุ submodules หรือ subpackages ซ้ำได้ ในทางเทคนิคแพ็คเกจเป็นโมดูล Python ที่มี__path__คุณสมบัติ
Martijn Pieters

556

ไฟล์ Python ใด ๆ เป็นโมดูลชื่อของมันคือชื่อฐานของไฟล์โดยไม่มี.pyนามสกุล แพคเกจคือชุดของโมดูลหลาม: ในขณะที่โมดูลเป็นไฟล์หลามเดียวแพคเกจเป็นไดเรกทอรีของโมดูลหลามที่มีการเพิ่ม__init__.pyไฟล์ที่จะแยกแยะแพคเกจจากไดเรกทอรีที่เพิ่งเกิดขึ้นจะมีพวงของงูหลามสคริปต์ที่ แพคเกจสามารถซ้อนในระดับความลึกใด ๆ โดยมีเงื่อนไขว่าไดเรกทอรีที่เกี่ยวข้องมี__init__.pyไฟล์ของตนเอง

ความแตกต่างระหว่างโมดูลและแพ็กเกจน่าจะอยู่ที่ระดับระบบไฟล์ เมื่อคุณนำเข้าโมดูลหรือแพ็คเกจวัตถุที่สอดคล้องกันที่สร้างขึ้นโดย Python จะเป็นประเภทmoduleเสมอ หมายเหตุอย่างไรก็ตามเมื่อคุณนำเข้าแพคเกจเฉพาะตัวแปร / ฟังก์ชั่น / คลาสใน__init__.pyไฟล์ของแพ็คเกจนั้นจะสามารถมองเห็นได้โดยตรงไม่ใช่แพ็กเกจย่อยหรือโมดูล เป็นตัวอย่างให้พิจารณาxmlแพ็คเกจในไลบรารีมาตรฐาน Python: xmlไดเรกทอรีประกอบด้วย__init__.pyไฟล์และไดเรกทอรีย่อยสี่ไดเรกทอรี ไดเรกทอรีย่อยetreeมี__init__.pyไฟล์และอื่น ๆ ในกลุ่มเป็นElementTree.pyไฟล์ ดูว่าเกิดอะไรขึ้นเมื่อคุณพยายามนำเข้าแพ็คเกจ / โมดูลแบบโต้ตอบ:

>>> import xml
>>> type(xml)
<type 'module'>
>>> xml.etree.ElementTree
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'etree'
>>> import xml.etree
>>> type(xml.etree)
<type 'module'>
>>> xml.etree.ElementTree
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'ElementTree'
>>> import xml.etree.ElementTree
>>> type(xml.etree.ElementTree)
<type 'module'>
>>> xml.etree.ElementTree.parse
<function parse at 0x00B135B0>

ใน Python มีโมดูลในตัวเช่นsysที่เขียนด้วยภาษา C แต่ฉันไม่คิดว่าคุณควรจะพิจารณาสิ่งเหล่านั้นในคำถามของคุณ


9
ขอบคุณที่กล่าวถึงอย่างชัดเจนว่าวัตถุที่เกี่ยวข้องที่สร้างโดย Python นั้นเป็นประเภทmoduleเสมอ ฉันกำลังอยู่ในขั้นตอนการเขียนดีบักเกอร์และเป็นห่วงว่าดีบักเกอร์ของฉันไม่ถูกต้องในการบอกว่าแพ็คเกจของฉันเป็นmoduleของ
ArtOfWarfare

8
@jolvi ไฟล์ Python ที่มีชื่อไฟล์ที่มีเครื่องหมายขีดคั่นยังคงสามารถนำเข้าเป็นโมดูลได้โดยไม่ต้องมีimportคำสั่งปกติเนื่องจากไม่อนุญาตให้ใช้เครื่องหมายขีดกลางในตัวระบุ Python ใช้importlib.import_module()แทน
Giulio Piancastelli

2
@jolvi ฉันไม่ได้ คุณกำลังอ่านอยู่ที่ไหนในความคิดเห็นของฉัน ฉันแค่บอกว่าถ้าคุณมีหรือสะดุดไฟล์ Python ที่มีเครื่องหมายขีดกลางในชื่อคุณจะยังสามารถนำเข้ามันเป็นโมดูล ฉันไม่ได้ทำคำสั่งเกี่ยวกับวิธีการตั้งชื่อไฟล์ Python ที่ต้องการ ฉันแน่ใจว่าคุณสามารถหาที่อื่นได้: โดยปกติแล้วเราขอแนะนำอย่างยิ่งให้หลีกเลี่ยงขีดกลางด้วยการขีดเส้นใต้
Giulio Piancastelli

3
เป็นสิ่งใหม่สำหรับ Python แพ็คเกจย่อยหรือโมดูลที่ไม่พร้อมใช้งานตามค่าเริ่มต้นเมื่อนำเข้าแพ็คเกจหลักคือสิ่งที่ทำให้ฉันสะดุด มีเหตุผลพิเศษไหม? และมีรูปแบบทั่วไปที่จะทำให้แพ็คเกจย่อยหรือโมดูลพร้อมใช้งาน (ผ่านชื่อเต็มของพวกเขา) เมื่อนำเข้าแพคเกจผู้ปกครอง?
2560

2
@sschuberth เพียงนำเข้าแพ็คเกจย่อยในinit .py ของแพ็คเกจหลัก
แอนนา

33

จากอภิธานศัพท์ Python :

โปรดทราบว่าแพ็กเกจทั้งหมดเป็นโมดูล แต่ไม่ใช่ทุกโมดูลเป็นแพ็กเกจ หรือใส่อีกวิธีหนึ่งแพ็คเกจเป็นเพียงโมดูลชนิดพิเศษ โดยเฉพาะโมดูลใด ๆ ที่มี__path__แอตทริบิวต์ถือเป็นแพคเกจ

ไฟล์ Python ที่มีเครื่องหมายขีดกลางในชื่อmy-file.pyไม่สามารถนำเข้าด้วยimportคำสั่งแบบง่าย รหัสฉลาดimport my-fileเหมือนกับimport my - fileที่จะยกข้อยกเว้น ไฟล์ดังกล่าวมีลักษณะดีเป็นสคริปต์ในขณะที่ไฟล์ importable เป็นโมดูล


23

อันดับแรกโปรดจำไว้ว่าในคำจำกัดความที่แม่นยำโมดูลนั้นเป็นวัตถุในหน่วยความจำของตัวแปล Python ซึ่งมักจะสร้างขึ้นโดยการอ่านไฟล์อย่างน้อยหนึ่งไฟล์จากดิสก์ แม้ว่าเราอาจเรียกไฟล์ดิสก์อย่างไม่เป็นทางการเช่นa/b/c.py"โมดูล" แต่มันจะไม่กลายเป็นไฟล์จริงจนกว่าจะรวมกับข้อมูลจากแหล่งอื่น ๆ (เช่นsys.path) เพื่อสร้างวัตถุโมดูล

(หมายเหตุเช่นโมดูลสองตัวที่มีชื่อแตกต่างกันสามารถโหลดได้จากไฟล์เดียวกันทั้งนี้ขึ้นอยู่กับsys.pathการตั้งค่าอื่น ๆ นี่คือสิ่งที่เกิดขึ้นพร้อมกับpython -m my.moduleตามด้วยimport my.moduleล่ามในนั้นจะมีสองโมดูลวัตถุ__main__และmy.moduleทั้งสองสร้าง จากไฟล์เดียวกันบนดิสก์, my/module.py.)

แพคเกจเป็นโมดูลที่อาจมี submodules (รวมถึงแพ็กเกจย่อยบริการ) บางโมดูลไม่สามารถทำสิ่งนี้ได้ ตัวอย่างเช่นสร้างลำดับชั้นของโมดูลขนาดเล็ก:

$ mkdir -p a/b
$ touch a/b/c.py

ตรวจสอบให้แน่ใจว่าไม่มีไฟล์อื่น ๆ aภายใต้ เริ่มล่าม Python 3.4 หรือใหม่กว่า (เช่นด้วยpython3 -i) และตรวจสอบผลลัพธ์ของข้อความสั่งต่อไปนี้:

import a
a                 <module 'a' (namespace)>
a.b               AttributeError: module 'a' has no attribute 'b'
import a.b.c
a.b               <module 'a.b' (namespace)>
a.b.c             <module 'a.b.c' from '/home/cjs/a/b/c.py'>

โมดูลaและa.bแพ็กเกจ (อันที่จริงแพคเกจบางชนิดเรียกว่า "แพ็คเกจเนมสเปซ" แม้ว่าเราจะไม่กังวลเกี่ยวกับสิ่งนี้) อย่างไรก็ตามโมดูลa.b.cไม่ใช่แพ็กเกจ เราสามารถสาธิตสิ่งนี้ได้โดยเพิ่มไฟล์อื่นa/b.pyเข้าไปในโครงสร้างไดเรกทอรีด้านบนและเริ่มล่ามสด:

import a.b.c
 ImportError: No module named 'a.b.c'; 'a.b' is not a package
import a.b
a                 <module 'a' (namespace)>
a.__path__        _NamespacePath(['/.../a'])
a.b               <module 'a.b' from '/home/cjs/tmp/a/b.py'>
a.b.__path__      AttributeError: 'module' object has no attribute '__path__'

Python รับประกันว่าโมดูลหลักทั้งหมดจะถูกโหลดก่อนที่จะโหลดโมดูลย่อย ข้างต้นนั้นพบว่าa/เป็นไดเรกทอรีและเพื่อสร้างแพคเกจใน namespace aและที่a/b.pyเป็นแฟ้มแหล่งที่มาหลามที่มันโหลดและการใช้ประโยชน์ในการสร้าง (ไม่ใช่แพ็คเกจ) a.bโมดูล ณ จุดนี้คุณไม่สามารถมีโมดูลได้a.b.cเนื่องจากa.bไม่ใช่แพ็คเกจดังนั้นจึงไม่มีโมดูล

คุณสามารถดูได้ที่นี่ว่าโมดูลแพ็กเกจaมี__path__แอตทริบิวต์ (แพ็คเกจต้องมีสิ่งนี้) แต่โมดูลที่ไม่ใช่แพ็กเกจa.bไม่มี


1
หากคุณยังไม่ได้กลับไปใช้ตัวอย่างในคำตอบนี้
Donal Lafferty

2

คำตอบที่ล่าช้า แต่มีคำจำกัดความอื่น:

แพคเกจถูกแสดงโดยเอนทิตีบนสุดที่นำเข้าซึ่งอาจเป็นโมดูลที่มีอยู่ในตัวเองหรือ__init__.pyโมดูลพิเศษเป็นเอนทิตี้อันดับต้น ๆ จากชุดของโมดูลภายในโครงสร้างไดเรกทอรีย่อย

ดังนั้นแพคเกจทางกายภาพคือหน่วยการกระจายซึ่งมีโมดูลหนึ่งโมดูลขึ้นไป


1
ฉันรู้สึกว่ามีคำจำกัดความสองประการสำหรับแพ็คเกจใน Python และมันแตกต่างกัน คำตอบของคุณดูเหมือนจะรวมเข้าด้วยกัน พูดอย่างเคร่งครัดแพคเกจหลามเป็นไดเรกทอรีที่มีให้__init__.pyภายในโมดูล แต่ถ้าคุณพูดคุยเกี่ยวกับหน่วยจำหน่าย (ปกติผ่าน PyPI) แล้วนี้เป็นอีกประเภทหนึ่งของแพคเกจทั้งหมด (โดยปกติจะกำหนดโดยการดำรงอยู่ของsetup.py) ฉันพบว่าการใช้คำทั้งสองนี้packageทำให้เกิดความสับสนและฉันได้พูดกับผู้เริ่มต้น Python บางคนที่พบว่ามันทำให้สับสนอย่างที่สุด
davidA

@davidA ไม่ใช่แค่ความรู้สึกของคุณ มันได้รับการประมวลผลแล้ว: packaging.python.org/glossary/#term-distribution-package (ขอบคุณสำหรับความกระจ่างเช่นกัน!)
Lorem Ipsum

0

แพคเกจนี้ยังเป็นโมดูลที่สามารถมีโมดูลอื่น ๆ 'โมดูลไฟล์ที่ใช้งานง่ายและแพ็คเกจ (แพ็คเกจย่อย)' รหัสที่เกี่ยวข้องกับประเภทแพคเกจของโมดูลเข้าไปใน__init__.pyไฟล์

import pack1
print(type(pack1))

ในขณะที่โมดูลเป็นไฟล์ง่าย ๆ ที่สามารถมีฟังก์ชั่น, คลาส, รหัสที่รันได้ ฯลฯ หลังจากนำเข้าโมดูลมันจะทำงานเหมือนวัตถุที่คุณสามารถเข้าถึงตัวระบุที่กำหนดไว้ในโมดูล

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.