41. Gói hàng

Một gói là một mô-đun có thể chứa các mô-đun khác. Trong CPython, gói không phải là một danh mục đối tượng riêng biệt. Nó vẫn là một đối tượng mô-đun, nhưng nó có siêu dữ liệu nhập cho hệ thống nhập biết nơi cần tìm các mô-đun con.

Ở cấp độ Python, thư mục này có thể là một gói:text app/ __init__.py config.py server.py Bạn có thể nhập nó dưới dạng:python import app import app.config from app.server import run Quy tắc quan trọng rất đơn giản:```text A package is a module with submodule search locations.


## 41.1 Gói là mô-đun

Mt đối tượng gói có loi`module`.

```python
import email

print(type(email))
print(email.__name__)
```Đầu ra:```text
<class 'module'>
email
```Mt gói có t đin mô-đun ging như bt k mô-đun nào khác.```python
import email

print(email.__dict__)
```Không gian tên gói lưu tr tên bình thường:```text
__name__
__doc__
__package__
__loader__
__spec__
__path__
__file__
__cached__
```Nó cũng có th lưu tr các hàm, lp, hng, mô-đun con đã nhp và tên API công khai được tái xut.

Do đó, mt gói là c hai:```text
a namespace object
a container for submodule lookup
```## 41.2 Vai trò của`__init__.py`Một gói thông thường thường có`__init__.py`tài liệu.```text
project/
    app/
        __init__.py
        config.py
        routes.py
```Khi CPython nhp`app`, nó thc thi:```text
app/__init__.py
```Mã trong`__init__.py`khi to không gian tên gói.

Ví d:```python
# app/__init__.py

VERSION = "1.0.0"

def create_app():
    return "app"
```Sau đó:```python
import app

print(app.VERSION)
print(app.create_app())
```tp tin`__init__.py`không ch là mt đim đánh du. Nó là mã mô-đun thc thi.

## 41.3 Nhập gói tối thiểu

Đối vi cách b trí này:```text
demo/
    __init__.py
    util.py
```và mã này:```python
import demo
```CPython thc hin đại khái:```text
find package named "demo"
create module object for "demo"
set package metadata
insert "demo" into sys.modules
execute demo/__init__.py
bind name "demo" in caller namespace
```Sau khi nhp:```python
import sys
import demo

print(sys.modules["demo"] is demo)
```Đầu ra:```text
True
```Gói được lưu tr trong`sys.modules`dưới tên đầy đủ ca nó.

## 41.4 Nhập mô-đun con

Dành cho:```python
import demo.util
```CPython nhp gói gc trước.

V mt khái nim:```text
import demo
then search demo.__path__ for util
then import demo.util
then set demo.util attribute
```Sau khi nhp thành công:```python
import demo.util

print(demo.util)
print(demo.util.__name__)
```Hình dng đầu ra:```text
<module 'demo.util' from '.../demo/util.py'>
demo.util
```Mô-đun con được lưu tr riêng:```python
import sys
import demo
import demo.util

print(sys.modules["demo"])
print(sys.modules["demo.util"])
print(demo.util is sys.modules["demo.util"])
```Gói cha và mô-đun con là các đối tượng mô-đun khác nhau.

## 41.5 Tên mô-đun đủ điều kiện

Các gói to tên mô-đun phân cp.```text
app
app.config
app.server
app.server.http
```Mi mô-đun được nhp có mt tên đủ điu kin.```python
import app.server.http

print(app.__name__)
print(app.server.__name__)
print(app.server.http.__name__)
```Đầu ra:```text
app
app.server
app.server.http
```Tên đủ điu kin là khóa được s dng trong`sys.modules`.

```python
import sys

print(sys.modules["app"])
print(sys.modules["app.server"])
print(sys.modules["app.server.http"])
```Danh tính da trên tên này rt quan trng. Nhp cùng mt tp dưới hai tên khác nhau có th to ra hai đối tượng mô-đun độc lp.

## 41.6 Địa điểm tìm kiếm gói hàng

Mt gói có`__path__`.

```python
import app

print(app.__path__)
```Đối vi gói thông thường,`__path__`thường cha thư mc gói.```text
['/path/to/project/app']
```Khi CPython nhp:```python
import app.config
```nó tìm kiếm`app.__path__`, không phi cp cao nht đầy đủ`sys.path`.

S khác bit này là trung tâm:

| Nhp khu | Đường dn tìm kiếm |
|---|---|
|`import app` | `sys.path` |
| `import app.config` | `app.__path__` |
| `import app.server.http` | `app.server.__path__`|

Mt gói kim soát nơi các gói con ca nó được tìm thy.

## 41,7`__spec__`và các gói

Mi mô-đun nhp khu hin đại đều có`__spec__`.

Đối vi các gói, thông s mô-đun bao gm các v trí tìm kiếm mô-đun con.```python
import app

print(app.__spec__)
print(app.__spec__.name)
print(app.__spec__.origin)
print(app.__spec__.submodule_search_locations)
```Đối vi mt gói, giá tr này thường không rng:```python
app.__spec__.submodule_search_locations
```Đối vi mt mô-đun thông thường, nó thường là`None`.

H thng nhp s dng điu này để phân bit các mô-đun vi các gói.

## 41.8 Siêu dữ liệu gói

Mt gói thông thường thường có các thuc tính sau:

| Thuc tính | Ý nghĩa |
|---|---|
|`__name__`| Tên gói đủ điu kin |
|`__package__`| Bi cnh gói được s dng để nhp tương đối |
|`__path__`| V trí tìm kiếm mô-đun con |
|`__spec__`| Đặc đim nhp khu |
|`__loader__`| Trình ti đã khi to gói |
|`__file__`| Đường dn đến`__init__.py`, nếu được h tr bng tp |
|`__cached__`| Đường dn ti mã byte được lưu trong b nh đệm, nếu có |

Ví d:```python
import json

print(json.__name__)
print(json.__package__)
print(json.__path__)
print(json.__file__)
print(json.__cached__)
```Do đó, mt gói có th được quan sát như mt đối tượng bình thường.

##41.9`__package__`các`__package__`thuộc tính kiểm soát độ phân giải nhập tương đối.

Bên trong`app/server.py`:

```python
from .config import Settings
```Du chm  đầu có nghĩa là:```text
resolve "config" relative to the current package
```Đối vi mô-đun`app.server`, bi cnh gói thường là:```text
app
```Vì thế:```python
from .config import Settings
```quyết tâm:```python
from app.config import Settings
```Đối vi mt mô-đun gói như`app`, `__package__`thường là`"app"`.

Đối vi mt mô-đun con như`app.server`, `__package__`thường là`"app"`.

Đối vi mt mô-đun con lng nhau như`app.http.server`, `__package__`thường là`"app.http"`.

## 41.10 Lệnh thực thi gói

Vi cách b trí này:```text
app/
    __init__.py
    config.py
    server.py
```và nhp khu này:```python
import app.server
```CPython thc thi theo th t sau:```text
1. app/__init__.py
2. app/server.py
```Nếu như`server.py`nhp khu`config.py`:

```python
# app/server.py
from . import config
```sau đó vic thc thi s tr thành:```text
1. app/__init__.py
2. app/server.py starts
3. app/config.py executes
4. app/server.py continues
```Các gói cha được ti trước các mô-đun con.

## 41.11 Thuộc tính gói cho mô-đun con

Sau:```python
import app.server
```gói cha thường nhn được mt thuc tính:```python
app.server
```Thuc tính này tr đến đối tượng mô hình con.

Chế độ xem tương đương:```python
import sys
import app.server

assert app.server is sys.modules["app.server"]
```Ràng buc này quan trng vì mã người dùng thường điu hướng qua các thuc tính gói:```python
import app.server

app.server.run()
```H thng nhp duy trì kết ni gia gói cha và mô-đun con.

## 41.12`import package.module`vs`from package import module`Hai hình thức này giống nhau nhưng không giống nhau về ràng buộc tên.```python
import app.config
```ràng buc`app`trong không gian tên người gi.```python
from app import config
```ràng buc`config`trong không gian tên người gi.

C hai đều ti bình thường`app.config`.

Ví d:```python
import app.config

print(app.config)
from app import config

print(config)
```Đối tượng -đun được tải thường giống nhau:```python
import app.config
from app import config

print(app.config is config)
```Đầu ra:```text
True
```Sự khác biệt  tên được đặt trong không gian tên của -đun nhập.

## 41.13 API gói công khai

Một gói  thể hiển thị API công khai sạch thông qua`__init__.py`.

Bố cục  dụ:```text
httpkit/
    __init__.py
    client.py
    response.py
    errors.py
```Các tập tin nội bộ:```python
# httpkit/client.py

class Client:
    ...
# httpkit/errors.py

class HTTPKitError(Exception):
    ...
```Mặt tiền:```python
# httpkit/__init__.py

from .client import Client
from .errors import HTTPKitError

__all__ = ["Client", "HTTPKitError"]
```Người dùng  thể viết:```python
from httpkit import Client, HTTPKitError
```Điều này cho phép tác giả gói thay đổi bố cục tệp nội bộ trong khi vẫn duy trì mục nhập công khai.

## 41,14`__all__`Tên`__all__`xác định tên công khai được sử dụng bởi nhập sao.```python
__all__ = ["Client", "HTTPKitError"]
```:```python
from httpkit import *
```Python nhập tên được liệt  trong`httpkit.__all__`.

Không `__all__`, gắn dấu sao importexport tên không bắt đầu bằng`_`.

`__all__`cũng hữu ích như tài liệu.  cho người đọc biết tên nào được dùng làm API gói công khai.

## 41.15 Mặt tiền trọn gói và chi phí nhập khẩu

Mặt tiền của gói cải thiện tính tiện dụng nhưng  thể tăng thời gian nhập khẩu.

Điều này rất thuận tiện:```python
# package/__init__.py

from .database import Database
from .server import Server
from .client import Client
from .analytics import Tracker
```Nhưng bây giờ:```python
import package
```tải tất cả các -đun đó.

Điều đó  thể tốn kém nếu các -đun đó nhập các phần phụ thuộc lớn, khởi tạo thư viện gốc, đọc tệp hoặc thực hiện cấu hình.

Mặt tiền gói nhẹ hơn  thể chỉ hiển thị những cái tên rẻ tiền:```python
# package/__init__.py

__version__ = "1.0.0"
```Sau đó người dùng nhập trực tiếp các thành phần nặng:```python
from package.client import Client
```Thiết kế gói tốt cân bằng giữa sự thuận tiện, thời gian khởi động  sự  ràng về sự phụ thuộc.

## 41.16 Thuộc tính gói lười biếng

Một gói  thể hiển thị các thuộc tính một cách lười biếng bằng cách sử dụng cấp độ -đun`__getattr__`.

```python
# package/__init__.py

def __getattr__(name):
    if name == "Client":
        from .client import Client
        return Client
    raise AttributeError(name)
```Sau đó:```python
import package

Client = package.Client
```nhập khẩu`.client`chỉ khi`Client`được yêu cầu.

Điều này  thể giảm chi phí khởi động trong khi vẫn duy trì API công khai tốt đẹp.

Sự đánh đổi  sự phức tạp. Việc xuất gói lười biếng khiến hành vi nhập ít  ràng hơn  lỗi  thể xuất hiện sau đó.

## 41.17 Gói thường

Một gói thông thường `__init__.py`.

```text
pkg/
    __init__.py
    mod.py
```Của cải:```text
executes __init__.py when imported
has __file__ pointing to __init__.py
has __path__ pointing to package directory
can define package-level API
can contain submodules and subpackages
```Hầu hết các gói ứng dụng  thư viện đều sử dụng các gói thông thường.

## 41.18 Gói không gian tên

Một gói không gian tên không  một`__init__.py`.

  thể được tập hợp từ nhiều thư mục.

 dụ:```text
dir1/
    plugins/
        alpha.py

dir2/
    plugins/
        beta.py
```Nếu cả hai`dir1``dir2`đang bật`sys.path`, sau đó`plugins` thể  một gói không gian tên.```python
import plugins.alpha
import plugins.beta
```Gói`plugins` thể  một`__path__`chứa cả hai vị trí.

Các gói không gian tên rất hữu ích khi  nhiều bản phân phối đóng góp vào một không gian tên gói.

## 41.19 Gói thông thường và Gói không gian tên

| Tính năng | Gói thông thường | Gói không gian tên |
|---|---|---|
| `__init__.py`|  | Không |
| Thực thi  khởi tạo gói |  | Không  trình khởi tạo duy nhất |
|  thể xác định trực tiếp tên cấp gói |  | Hạn chế |
|  thể mở rộng nhiều thư mục | Thường thì không |  |
| Sử dụng phổ biến | Thư viện  ứng dụng thông thường | Không gian tên plugin, phân phối phân chia |

Một gói thông thường cung cấp sự khởi tạo  ràng. Gói không gian tên mang lại sự kết hợp linh hoạt.

## 41.20 Gói con

Gói con  gói bên trong gói khác.```text
app/
    __init__.py
    api/
        __init__.py
        users.py
        posts.py
```Bạn  thể nhập:```python
import app.api
import app.api.users
```Hệ thống nhập khẩu giải quyết từng cấp độ.```text
app
app.api
app.api.users
```Mỗi cấp độ  đối tượng -đun riêng `sys.modules`lối vào.```python
import sys
import app.api.users

print(sys.modules["app"])
print(sys.modules["app.api"])
print(sys.modules["app.api.users"])
```## 41.21 Nhập khẩu tương đối trong gói

Nhập khẩu tương đối  phổ biến bên trong các gói.```python
from .config import Settings
from .storage import Store
from ..core import errors
```Dấu chấm  nghĩa  mức độ:

|  pháp | Ý nghĩa |
|---|---|
|`from . import x`| Nhập anh chị em từ gói hiện tại |
|`from .x import y`| Nhập từ -đun con trong gói hiện tại |
|`from .. import x`| Nhập từ gói gốc |
|`from ..x import y`| Nhập từ anh chị em theo gói gốc |

Việc nhập tương đối làm cho các phần phụ thuộc nội bộ trở nên độc lập với tên gói cấp cao nhất.

Họ cũng yêu cầu bối cảnh gói chính xác. Chạy trực tiếp một tập tin gói  thể làm hỏng chúng.

## 41.22 Vấn đề thực thi tập lệnh trực tiếp

Cho:```text
app/
    __init__.py
    main.py
    config.py
```Bên trong`main.py`:

```python
from .config import Settings
```Điều này hoạt động:```bash
python -m app.main
```Điều này  thể thất bại:```bash
python app/main.py
```Khi được thực thi bằng đường dẫn tệp,`main.py`trở thành`__main__`, không`app.main`. -đun  thể mất bối cảnh gói cần thiết cho việc nhập tương đối.

Sử dụng thực thi -đun cho  gói:```bash
python -m app.main
```## 41,23`__main__.py`Một gói có thể định nghĩa`__main__.py`.

```text
tool/
    __init__.py
    __main__.py
    cli.py
```Sau đó:```bash
python -m tool
```thực thi:```text
tool/__main__.py
``` dụ:```python
# tool/__main__.py

from .cli import main

main()
```Đây  cách tiêu chuẩn để làm cho một gói  thể thực thi được.

## 41.24 Tác dụng phụ khi khởi tạo gói

Bởi `__init__.py`thực thi trong quá trình nhập, việc khởi tạo gói  thể  tác dụng phụ.```python
# package/__init__.py

print("loading package")
connect_to_service()
```Sau đó:```python
import package
```thực hiện ngay công việc đó.

Điều này  thể gây rắc rối cho:```text
startup time
tests
CLI responsiveness
server cold starts
configuration ordering
optional dependencies
import cycles
```Giữ khởi tạo gói nhỏ khi  thể.

Tốt`__init__.py`các tập tin thường chứa:```text
version constants
cheap re-exports
small compatibility shims
public API declarations
```Tránh làm việc nặng nhọc trừ khi việc khởi tạo thời gian nhập  một phần của hợp đồng gói  ràng.

## 41.25 Đồ thị phụ thuộc gói

Cấu trúc gói phải phản ánh hướng phụ thuộc.

Một ứng dụng sạch  thể trông giống như:```text
app/
    __init__.py
    main.py
    config.py
    domain/
        __init__.py
        users.py
        posts.py
    storage/
        __init__.py
        db.py
    web/
        __init__.py
        routes.py
```Hướng phụ thuộc tốt:```text
main imports web
web imports domain
web imports storage
storage imports domain
domain imports no app-specific infrastructure
```Hướng phụ thuộc kém:```text
domain imports web
storage imports main
config imports route handlers
__init__.py imports everything
```Biểu đồ phụ thuộc gói không hợp lệ thường tạo ra hoạt động nhập vòng tròn.

## 41.26 Nhập khẩu theo gói theo vòng tròn

Nhập vòng tròn  phổ biến trong các gói  các -đun thường nhập anh chị em.

 dụ:```python
# app/users.py
from .posts import Post

class User:
    ...
# app/posts.py
from .users import User

class Post:
    ...
```Điều này  thể thất bại `app.users``app.posts`cần nhau trong quá trình thực thi cấp cao nhất.

Các bản sửa lỗi  thể:

Di chuyển các định nghĩa được chia sẻ:```text
app/
    models.py
    users.py
    posts.py
```Sử dụng nhập khẩu địa phương:```python
def create_post():
    from .posts import Post
    return Post()
```Sử dụng nhập khẩu chỉ kiểm tra loại:```python
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from .posts import Post
```Việc sửa chữa cấu trúc thường  tốt nhất. Nhập khẩu tuần hoàn thường chỉ ra rằng ranh giới -đun cần điều chỉnh.

## 41.27 Tái xuất cấp gói và nhập khẩu tuần hoàn

Tái xuất quá nhiều vào`__init__.py` thể tạo ra chu kỳ nhập khẩu.

 dụ:```python
# app/__init__.py
from .server import Server
from .config import Config
```Sau đó bên trong`server.py`:

```python
from app import Config
```Lực lượng này`app.__init__`để hoàn thành trong khi  vẫn đang nhập`server`.

Nhập khẩu nội bộ an toàn hơn :```python
from .config import Config
```Bên trong một gói, ưu tiên nhập từ -đun xác định hơn  từ mặt tiền của gói.

Mặt tiền gói chủ yếu dành cho người dùng bên ngoài.

## 41.28 Mô-đun riêng

Python sử dụng quy ước đặt tên cho các -đun riêng .```text
package/
    __init__.py
    public.py
    _internal.py
    _compat.py
```Dấu gạch dưới  đầu  nghĩa  -đun này  -đun nội bộ theo quy ước.```python
from package._internal import helper
```Điều này được cho phép, nhưng tác giả gói  thể thay đổi`_internal` không bảo toàn tính tương thích.

API công khai phải được ghi lại  tái xuất một cách  chủ ý.

## 41,29`src`Bố cục

Nhiều dự án Python sử dụng`src`cách trình bày.```text
project/
    pyproject.toml
    src/
        package/
            __init__.py
            core.py
    tests/
        test_core.py
```Bố cục này giúp ngăn chặn việc nhập ngẫu nhiên từ thư mục gốc của kho lưu trữ.

Không `src`, các cuộc kiểm tra  thể  tình nhập các tệp cục bộ ngay cả khi gói đã cài đặt bị hỏng.

Với`src`, gói phải được cài đặt hoặc đường dẫn phải được cấu hình chính xác, phù hợp hơn với hành vi của người dùng.

## 41.30 Dữ liệu gói

Các gói  thể chứa các tập tin dữ liệu.```text
package/
    __init__.py
    templates/
        page.html
    data/
        defaults.json
```Đừng cho rằng dữ liệu gói nằm trong thư mục hệ thống tệp thông thường. Các gói  thể được nhập từ tệp zip hoặc các trình tải khác.

Thích hơn`importlib.resources`:

```python
from importlib.resources import files

data = files("package.data").joinpath("defaults.json").read_text()
```Điều này yêu cầu hệ thống nhập tài nguyên thay  xây dựng đường dẫn thủ công từ`__file__`.

## 41,31`__file__`Hạn chế

Nhiều gói `__file__`.

```python
import package

print(package.__file__)
```Nhưng  mạnh mẽ không nên cho rằng tất cả các -đun  gói đều  đường dẫn tệp bình thường.

Một số -đun  thể :```text
built in
frozen
loaded from zip files
loaded by custom importers
namespace packages
```Đối với tài nguyên gói, hãy sử dụng API hệ thống nhập thay  số học đường dẫn trực tiếp khi  thể.

## 41.32 Giá trị phiên bản gói

Các gói thường xác định`__version__`.

```python
# package/__init__.py

__version__ = "1.2.3"
```Điều này  đơn giản  phổ biến.

Một gói cũng  thể sử dụng siêu dữ liệu gói đã cài đặt:```python
from importlib.metadata import version

__version__ = version("package-name")
```Cách tiếp cận thứ hai tránh trùng lặp chuỗi phiên bản nhưng  thể thất bại nếu gói không được cài đặt làm siêu dữ liệu.

Đối với các thư viện, hãy đảm bảo việc xử  phiên bản luôn đơn giản   thể dự đoán được.

## 41.33 Độ ổn định của API công khai

Bố cục gói  API công khai  những thứ khác nhau.

Bố trí bên trong:```text
library/
    _client.py
    _transport.py
    _errors.py
```API công khai:```python
from library import Client, LibraryError
```Gói  thể bảo toàn API công khai trong khi thay đổi nội bộ.```python
# library/__init__.py

from ._client import Client
from ._errors import LibraryError

__all__ = ["Client", "LibraryError"]
```Sự tách biệt này mang lại cho người bảo trì quyền tự do tái cấu trúc.

## 41.34 Thời gian nhập gói

Thời gian nhập gói  thể được đo.```bash
python -X importtime -c "import package"
```Nhập hàng trọn gói chậm thường xuất phát từ:```text
large transitive imports
heavy package __init__.py files
runtime configuration loading
native library initialization
network or file-system work
plugin auto-discovery
large type-hint imports at runtime
```Sự cải tiến thường bắt đầu bằng việc thực hiện`__init__.py`nhỏ hơn.

## 41.35 Mẫu khởi tạo gói

Một thực tế`__init__.py`thường trông giống như:```python
"""
Public API for examplekit.
"""

from .client import Client
from .errors import ExampleKitError

__all__ = [
    "Client",
    "ExampleKitError",
]

__version__ = "0.1.0"
```Điều này  thể chấp nhận được khi`client``errors`nhập khẩu rẻ.

Đối với các -đun nặng hơn:```python
__all__ = ["Client", "ExampleKitError", "__version__"]

__version__ = "0.1.0"

def __getattr__(name):
    if name == "Client":
        from .client import Client
        return Client
    if name == "ExampleKitError":
        from .errors import ExampleKitError
        return ExampleKitError
    raise AttributeError(name)
```Chỉ sử dụng hình thức lười biếng khi chi phí khởi động cao hơn mức độ phức tạp.

## 41.36 Gói và Nội bộ CPython

 cấp độ CPython, nhập gói vẫn  nhập -đun.

Sự khác biệt xuất hiện trong siêu dữ liệu nhập:```text
regular module:
    __spec__.submodule_search_locations = None

package:
    __spec__.submodule_search_locations = [...]
    __path__ = [...]
```Khi nhập -đun con, hệ thống nhập sẽ sử dụng đường dẫn gói cha.

Đơn giản hóa:```python
def import_child(parent, child_name):
    fullname = parent.__name__ + "." + child_name
    path = parent.__path__
    spec = find_spec(fullname, path)
    return load(spec)
```Hệ thống nhập thực sự xử  các giao thức khóa, lỗi, gói vùng tên, bộ nhớ đệm  trình tải.

## 41.37 Đối tượng gói và Tra cứu thuộc tính

Đối tượng gói sử dụng tra cứu thuộc tính -đun thông thường.```python
import package

package.name
```Điều này sẽ tìm trong từ điển gói.

Nếu một gói xác định cấp độ -đun`__getattr__`, các thuộc tính bị thiếu  thể được tính toán linh hoạt.```python
def __getattr__(name):
    ...
```Nhưng các -đun con được nhập vào thường được lưu trữ dưới dạng thuộc tính trên gói gốc.```python
import package.submodule

print(package.submodule)
```Đây   do tại sao không gian tên gói  thể phát triển khi quá trình nhập diễn ra.

## 41.38 Nhập gói không thành công

Nếu quá trình nhập gói không thành công trong quá trình`__init__.py`, hệ thống nhập sẽ loại bỏ -đun bị lỗi khỏi`sys.modules`trong nhiều trường hợp.

 dụ:```python
# broken/__init__.py

raise RuntimeError("failed")
```Sau đó:```python
import broken
```đưa ra một ngoại lệ.

Hệ thống nhập phải tránh để -đun bị hỏng được lưu vào bộ nhớ đệm như thể  đã được khởi tạo thành công.

Lỗi -đun con  thể tinh vi hơn. Gói cha  thể nhập thành công trong khi -đun con không thành công.

## 41.39 Nhận dạng gói trùng lặp

Nhận dạng gói trùng lặp xảy ra khi  thể nhập cùng một gói dưới các tên khác nhau.

Vấn đề về đường dẫn  dụ:```text
project/
    app/
        __init__.py
        state.py
```Một đường dẫn nhập:```python
import app.state
```Một con đường tình cờ khác:```python
import state
```Bây giờ cùng một tệp  thể được tải hai lần:```text
sys.modules["app.state"]
sys.modules["state"]
```Hậu quả:```text
two module global dictionaries
two singleton instances
two registry objects
two class identities
two caches
```Đây  nguồn phổ biến của các lỗi lạ.

Tránh  bằng cách sử dụng nhập gói tuyệt đối nhất quán  tránh không an toàn`sys.path`chỉnh sửa.

## 41.40 Gói và Nhận dạng loại

Lớp  các đối tượng được lưu trữ trong các -đun.

Nếu cùng một -đun được nhập hai lần dưới các tên khác nhau thì các lớp của  sẽ được tạo hai lần.```python
# app/models.py

class User:
    pass
```Nếu được tải như cả hai:```python
import app.models
import models
```sau đó:```python
app.models.User is models.User
``` lẽ:```text
False
```Một đối tượng được tạo từ một lớp  thể bị lỗi`isinstance`kiểm tra đối với người khác.

Đây   do tại sao việc nhận dạng -đun lại quan trọng đối với các gói.

## 41.41 Gói và Điểm vào

Các gói đã cài đặt  thể hiển thị tập lệnh bảng điều khiển thông qua siêu dữ liệu đóng gói.

Một điểm vào dòng lệnh  thể gọi:```text
package.module:function
```Về mặt khái niệm, việc chạy lệnh sẽ nhập -đun  gọi hàm.

Mục tiêu  dụ:```python
# tool/cli.py

def main():
    ...
```Điểm vào:```text
tool = tool.cli:main
```Điều này  nghĩa  chi phí khởi động CLI bao gồm việc nhập`tool.cli` sự phụ thuộc của .

Giữ các -đun nhập CLI  mức nhỏ khi khởi động.

## 41.42 Gói plugin

Các gói thường hỗ trợ plugin.

Kiến trúc plugin  thể sử dụng:```text
namespace packages
entry points
importlib
explicit plugin lists
dynamic discovery
```Trình tải plugin  ràng đơn giản:```python
import importlib

def load_plugin(name):
    return importlib.import_module(f"app_plugins.{name}")
```Hệ thống plugin dựa trên gói nên tránh nhập mọi plugin một cách háo hức trừ khi cần thiết.

Việc nhập plugin thường  tác dụng phụ, chẳng hạn như đăng .```python
# plugin_alpha.py

from registry import register

register("alpha", handler)
```Điều này hữu ích nhưng lệnh nhập sẽ trở thành một phần của hoạt động của chương trình.

## 41.43 Gói và tên phân phối

Tên gói nhập  tên gói phân phối  thể khác nhau.

 dụ:```text
distribution name: beautifulsoup4
import name: bs4
```Hệ thống nhập biết tên nhập. Công cụ đóng gói biết tên phân phối.

Sự khác biệt này quan trọng khi đọc danh sách phụ thuộc hoặc siêu dữ liệu gói.```python
import bs4
```không nói bản phân phối đã cài đặt đã được đặt tên`bs4`.

## 41.44 Gói và`pyproject.toml`Các gói Python hiện đại thường được sử dụng`pyproject.toml`.

Một dự án  thể tuyên bố:```toml
[project]
name = "examplekit"
version = "0.1.0"
```Tên phân phối `examplekit`.

Gói nhập khẩu  thể :```text
src/examplekit/
    __init__.py
```Đối với hệ thống nhập của CPython, chỉ bố cục gói  thể nhập `sys.path`vấn đề trong thời gian chạy. Xây dựng các vấn đề về siêu dữ liệu trước khi chạy, trong quá trình cài đặt  đóng gói.

## 41,45 Số lượt cài đặt có thể chỉnh sửa

Trong quá trình phát triển, các gói thường được cài đặt  chế độ  thể chỉnh sửa.```bash
python -m pip install -e .
```Điều này làm cho gói  thể được nhập từ cây đang làm việc.

Từ quan điểm của CPython, kết quả vẫn  nhập dựa trên đường dẫn. Môi trường đã được định cấu hình để vị trí nguồn gói xuất hiện  độ phân giải nhập.

Các bản cài đặt  thể chỉnh sửa giúp kiểm tra  các công cụ nhập gói như người dùng thực hiện trong khi vẫn sử dụng các tệp nguồn trực tiếp.

## 41.46 Danh sách kiểm tra gỡ lỗi gói

Khi quá trình nhập gói hoạt động kỳ lạ, hãy kiểm tra:```python
import sys
import package

print(package)
print(package.__name__)
print(getattr(package, "__file__", None))
print(getattr(package, "__path__", None))
print(package.__spec__)
print(sys.modules.get("package"))
```Đối với một -đun con:```python
import package.submodule

print(package.submodule)
print(package.submodule.__name__)
print(package.submodule.__file__)
print(sys.modules.get("package.submodule"))
```Đối với các vấn đề về đường dẫn:```python
import sys

for p in sys.path:
    print(p)
```Để giải quyết  không cần nhập:```python
import importlib.util

print(importlib.util.find_spec("package"))
print(importlib.util.find_spec("package.submodule"))
```## 41.47 Mô hình nhập gói tối thiểu

Một  hình đơn giản hóa để nhập -đun con:```python
def import_package_child(parent_name, child_name):
    parent = import_module(parent_name)

    fullname = parent_name + "." + child_name

    if fullname in sys.modules:
        return sys.modules[fullname]

    spec = find_spec(fullname, parent.__path__)
    if spec is None:
        raise ModuleNotFoundError(fullname)

    module = module_from_spec(spec)
    sys.modules[fullname] = module

    try:
        spec.loader.exec_module(module)
    except Exception:
        del sys.modules[fullname]
        raise

    setattr(parent, child_name, module)
    return module
``` hình này nắm bắt các phần cụ thể của gói:```text
load parent first
search parent.__path__
cache child by fully qualified name
bind child as parent attribute
```##41.48 Quy tắc thiết kế bao bì tốt

Giữ khởi tạo gói nhỏ.

Sử dụng`__init__.py`để hiển thị API công khai ổn định, không chạy ứng dụng.

Ưu tiên nhập nội bộ trực tiếp từ việc xác định các -đun.

Tránh nhập -đun nội bộ từ mặt tiền gói.

Tránh thực thi tập lệnh trực tiếp cho các -đun gói. Sử dụng`python -m package.module`.

Sử dụng`importlib.resources`cho dữ liệu gói.

Sử dụng`__all__`để ghi lại tên công cộng.

Chỉ sử dụng các gói không gian tên khi cần thành phần gói chia nhỏ.

Tránh sửa đổi`sys.path` gói bên trong.

Giữ hướng phụ thuộc  ràng.

## 41.49 Lỗi gói phổ biến

| Triệu chứng | Nguyên nhân  thể |
|---|---|
| Nhập tương đối không thành công | -đun chạy dưới dạng tập lệnh thay  bằng`-m`|
| Gói khởi tạo một phần | Nhập khẩu tuần hoàn |
| Thiếu thuộc tính gói | -đun con chưa được nhập hoặc mặt tiền không xuất  |
| Hai trường hợp đơn lẻ | Cùng một -đun được nhập dưới hai tên |
| Chậm`import package`| Nặng`__init__.py`hoặc nhập khẩu chuyển tiếp |
| Hoạt động trong repo nhưng không thành công sau khi cài đặt | Bố cục gói không hợp lệ hoặc thiếu dữ liệu gói |
| -đun tiêu chuẩn bị che khuất | Xung đột tên gói hoặc tệp cục bộ |

## 41,50 Điểm chính

Một gói  một -đun  các vị trí tìm kiếm -đun con.

Một gói thông thường thực thi`__init__.py`trong quá trình nhập khẩu.

Các -đun con được lưu trữ trong`sys.modules`dưới những cái tên đầy đủ.

Một gói`__path__`kiểm soát nơi các -đun con được tìm kiếm.

Nhập khẩu tương đối phụ thuộc vào bối cảnh gói.

Các gói không gian tên cho phép một không gian tên gói trải rộng trên nhiều vị trí.

Mặt tiền gói cải thiện tính công thái học của API nhưng  thể làm tăng chi phí nhập khẩu  tạo ra chu kỳ.

Hầu hết các lỗi gói đều đến từ quá trình nhập vòng tròn, lỗi đường dẫn, nhận dạng -đun trùng lặp, khởi tạo nặng hoặc chạy trực tiếp các tệp gói.