Giới thiệu
Trong Python, 2 khái niệm quan trọng mà có thể nhiều bạn không quá để ý nhưng lại rất quan trọng, đặc biệt khi tìm hiểu sâu về ngôn ngữ này, đó là “module” và “package”.
Thông thường, trong Python, khi ta biết trước mình cần sử dụng module nào, ta chỉ cần sử dụng import để load vào module đó. Tuy nhiên với những trường hợp nâng cao hơn, khi ta cần làm việc với module, ví dụ thực hiện những thao tác import, reload module một cách linh hoạt hơn, ta cần một thư viện để hỗ trợ việc này.
Package importlib trong Python cung cấp một API để thao tác với các modules một cách dynamically, cho phép lập trình viên import, reload, tra thông tin về module hay làm việc với Loader, Finder một cách linh hoạt hơn so với sử dụng import thông thường.
Package importlib
Giả sử trong khi chạy một chương trình Python, ta muốn load vào một module X, nhưng module này chỉ được xác định khi chạy chương trình đó (at runtime), hay tên của module nằm trong một biến a nào đó. Lúc này, ta không thể sử dụng import a được, vì điều này tương đương với việc ta muốn load vào module a:
>>> module_name = 'random'
>>> import module_name
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'module_name'
Lúc này, ta có thể sử dụng importlib để làm việc này:
>>> import importlib
>>> importlib
<module 'importlib' from '/home/hoang/miniconda3/lib/python3.12/importlib/__init__.py'>
Nhìn vào representation, ta có thể thấy package này nằm trên disk và ta có thể xem source code của nó ở vị trí đó nếu cần.
Một function quan trọng trong importlib có thể giúp ta load một module nào đó là importlib.import_module:
>>> importlib.import_module(module_name)
<module 'random' from '/home/hoang/miniconda3/lib/python3.12/random.py'>
Sau khi gọi import_module, module random sẽ được compile, execute và cache vào trong sys.modules:
>>> import sys
>>> 'random' in sys.modules
True
Tuy module này đã được load, vẫn còn thiếu một thứ để ta có thể sử dụng nó, đó là một handle, hay nói cách khác một variable name để ta sử dụng trong namespace hiện tại.
>>> random.uniform(0, 1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'random' is not defined. Did you forget to import 'random'?
Để sử dụng được module đã import, ta có thể dùng giá trị trả về với import_module:
>>> rd = importlib.import_module(module_name)
>>> rd.uniform(0, 1)
0.060102547843836174
importlib trong code thực tế
Sau đây là một ví dụ về việc sử dụng importlib mà mình đã gặp khi làm việc với Python. Ở đây, context là ta cần upgrade tree-sitter lên một version mới, nhưng trong code base hiện tại lại sử dụng một package khác, package này sử dụng version cũ của tree-sitter.
Nếu bạn chưa biết, Tree-sitter là một thư viện phân tích cú pháp (parser) mạnh mẽ, được thiết kế để thực hiện trích xuất cú pháp nhanh và chính xác cho các ngôn ngữ lập trình. Tree-sitter có một core chung, nhưng để hỗ trợ từng ngôn ngữ cụ thể, nó cần grammar riêng biệt. Các module như tree-sitter-python, tree-sitter-java, … chính là các package chứa các yếu tố cụ thể cho từng ngôn ngữ, chẳng hạn grammar,…
Ở ví dụ này, ta sử dụng importlib để tạo một compatibility layer, load vào những package riêng biệt cho từng ngôn ngữ lập trình một cách on-demand, nghĩa là khi nào cần thì ta sẽ load, dựa trên tên của package đó. Bạn có thể xem code snippet cụ thể tại đây.
Cảm ơn mọi người đã đọc bài. Hẹn gặp bạn ở bài viết tiếp theo!

Bình luận về bài viết này