Vietnamese (Tiếng Việt) translation by Dai Phong (you can also view the original English article)
Python là một ngôn ngữ lập trình tuyệt vời, nhưng việc đóng gói là một trong những điểm yếu nhất của nó. Nó là một thực tế được nhiều người biết đến trong cộng đồng. Việc cài đặt, import, sử dụng và tạo các gói đã được cải thiện rất nhiều trong nhiều năm qua, nhưng nó vẫn chưa bằng với những ngôn ngữ mới như Go và Rust những ngôn ngữ đã học hỏi rất nhiều từ những bất cập của Python và các ngôn ngữ lâu đời khác.
Trong bài hướng dẫn này, bạn sẽ học mọi thứ cần biết liên quan đến viết, đóng gói và phân phối các gói của riêng bạn.
Cách viết một Thư viện Python
Thư viện Python là bộ liên kết các mô đun Python được tổ chức dưới dạng một gói Python. Nói chung, điều đó có nghĩa là tất cả các mô-đun nằm trong cùng một thư mục và rằng thư mục này nằm trên đường dẫn tìm kiếm Python.
Hãy viết nhanh một gói Python 3 nho nhỏ và làm rõ tất cả những khái niệm này.
Pathology Package
Python 3 có một đối tượng Path tuyệt vời, là một cải tiến lớn so với mô-đun os.path vụng về của Python 2. Nhưng nó thiếu một khả năng quan trọng - tìm kiếm đường dẫn của script hiện tại. Điều này là rất quan trọng khi bạn muốn xác định vị trí của các tập tin truy cập liên quan đến script hiện tại.
Trong nhiều trường hợp, script có thể được cài đặt ở bất kỳ vị trí nào, vì vậy bạn không thể sử dụng các đường dẫn tuyệt đối, và thư mục hiện hành có thể được thiết lập thành bất kỳ giá trị nào, vì vậy bạn không thể sử dụng một đường dẫn tương đối. Nếu bạn muốn truy cập vào một tập tin trong một thư mục con hoặc thư mục cha, bạn phải có khả năng tìm ra thư mục của script hiện tại.
Đây là cách bạn làm điều đó trong Python:
import pathlib script_dir = pathlib.Path(__file__).parent.resolve()
Để truy cập vào một tập tin có tên là 'file.txt' trong một thư mục con 'data' trong thư mục hiện tại của script, bạn có thể sử dụng code sau: print(open(str(script_dir/'data/file.txt').read())
Với pathology package, bạn có một phương thức script_dir được tích hợp sẵn, và bạn sử dụng nó như sau:
from pathology.Path import script_dir print(open(str(script_dir()/'data/file.txt').read())
Vâng, nó có chút xíu thôi. Pathology package rất đơn giản. Nó dẫn xuất lớp Path của riêng nó từ Path của pathlib và thêm một hàm tĩnh script_dir() luôn trả về đường dẫn của script đang gọi.
Dưới đây là phần cài đặt:
import pathlib import inspect class Path(type(pathlib.Path())): @staticmethod def script_dir(): print(inspect.stack()[1].filename) p = pathlib.Path(inspect.stack()[1].filename) return p.parent.resolve()
Do việc cài đặt đa nền tảng của pathlib.Path, bạn có thể dẫn xuất trực tiếp từ nó và phải dẫn xuất từ một lớp con cụ thể (PosixPath hoặc WindowsPath). Sự phân giải thư mục script sử dụng mô-đun inspect để tìm script gọi và sau đó thuộc tính tên tập tin của nó.
Thử nghiệm Pathology Package
Bất cứ khi nào bạn viết ra một cái gì đó quan trọng, bạn nên kiểm thử nó. Mô-đun pathology cũng không ngoại lệ. Dưới đây là các bài kiểm tra sử dụng framework unit test tiêu chuẩn:
import os import shutil from unittest import TestCase from pathology.path import Path class PathTest(TestCase): def test_script_dir(self): expected = os.path.abspath(os.path.dirname(__file__)) actual = str(Path.script_dir()) self.assertEqual(expected, actual) def test_file_access(self): script_dir = os.path.abspath(os.path.dirname(__file__)) subdir = os.path.join(script_dir, 'test_data') if Path(subdir).is_dir(): shutil.rmtree(subdir) os.makedirs(subdir) file_path = str(Path(subdir)/'file.txt') content = '123' open(file_path, 'w').write(content) test_path = Path.script_dir()/subdir/'file.txt' actual = open(str(test_path)).read() self.assertEqual(content, actual)
Đường dẫn Python
Các gói Python phải được cài đặt ở đâu đó trên đường dẫn tìm kiếm của Python để được import bởi các mô-đun của Python. Đường dẫn tìm kiếm của Python là một danh sách các thư mục và luôn có sẵn trong sys.path
. Dưới đây là sys.path hiện tại của tôi:
>>> print('\n'.join(sys.path)) /Users/gigi.sayfan/miniconda3/envs/py3/lib/python36.zip /Users/gigi.sayfan/miniconda3/envs/py3/lib/python3.6 /Users/gigi.sayfan/miniconda3/envs/py3/lib/python3.6/lib-dynload /Users/gigi.sayfan/miniconda3/envs/py3/lib/python3.6/site-packages /Users/gigi.sayfan/miniconda3/envs/py3/lib/python3.6/site-packages/setuptools-27.2.0-py3.6.egg
Lưu ý dòng trống đầu tiên của đầu ra đại diện cho thư mục hiện tại, vì vậy bạn có thể import các mô-đun từ thư mục hiện hành, bất kể nó là gì. Bạn có thể trực tiếp thêm hoặc xóa bớt các thư mục trong sys.path.
Bạn cũng có thể định nghĩa một biến môi trường PYTHONPATH, và có một vài cách khác để kiểm soát nó. site-packages
tiêu chuẩn được bao gồm mặc định, và đây là nơi các gói mà bạn cài đặt bằng cách sử dụng pip go.
Cách Đóng gói một Thư viện Python
Bây giờ chúng ta có code và các bài kiểm tra, hãy đóng gói tất cả nó thành một thư viện thích hợp. Python cung cấp một cách dễ dàng thông qua mô-đun setup. Bạn tạo một tập tin có tên là setup.py trong thư mục gốc của gói. Sau đó, để tạo một phân phối nguồn (source distribution), bạn chạy lệnh: python setup.py sdist
Để tạo ra một phân phối nhị phân (binary distribution) gọi là wheel, bạn chạy lệnh: python setup.py bdist_wheel
Đây là tập tin setup.py của pathology package:
from setuptools import setup, find_packages setup(name='pathology', version='0.1', url='https://github.com/the-gigi/pathology', license='MIT', author='Gigi Sayfan', author_email='the.gigi@gmail.com', description='Add static script_dir() method to Path', packages=find_packages(exclude=['tests']), long_description=open('README.md').read(), zip_safe=False)
Nó bao gồm rất nhiều metadata bên cạnh phần tử 'packages' sử dụng hàm find_packages()
được import từ setuptools
để tìm ra các gói phụ.
Hãy xây dựng một phân phối nguồn:
$ python setup.py sdist running sdist running egg_info creating pathology.egg-info writing pathology.egg-info/PKG-INFO writing dependency_links to pathology.egg-info/dependency_links.txt writing top-level names to pathology.egg-info/top_level.txt writing manifest file 'pathology.egg-info/SOURCES.txt' reading manifest file 'pathology.egg-info/SOURCES.txt' writing manifest file 'pathology.egg-info/SOURCES.txt' warning: sdist: standard file not found: should have one of README, README.rst, README.txt running check creating pathology-0.1 creating pathology-0.1/pathology creating pathology-0.1/pathology.egg-info copying files to pathology-0.1... copying setup.py -> pathology-0.1 copying pathology/__init__.py -> pathology-0.1/pathology copying pathology/path.py -> pathology-0.1/pathology copying pathology.egg-info/PKG-INFO -> pathology-0.1/pathology.egg-info copying pathology.egg-info/SOURCES.txt -> pathology-0.1/pathology.egg-info copying pathology.egg-info/dependency_links.txt -> pathology-0.1/pathology.egg-info copying pathology.egg-info/not-zip-safe -> pathology-0.1/pathology.egg-info copying pathology.egg-info/top_level.txt -> pathology-0.1/pathology.egg-info Writing pathology-0.1/setup.cfg creating dist Creating tar archive removing 'pathology-0.1' (and everything under it)
Cảnh báo là do tôi đã sử dụng tập tin README.md không tiêu chuẩn. Không sao, chúng ta có thể bỏ qua nó. Kết quả là một tập tin tar-gzipped nằm trong thư mục dist:
$ ls -la dist total 8 drwxr-xr-x 3 gigi.sayfan gigi.sayfan 102 Apr 18 21:20 . drwxr-xr-x 12 gigi.sayfan gigi.sayfan 408 Apr 18 21:20 .. -rw-r--r-- 1 gigi.sayfan gigi.sayfan 1223 Apr 18 21:20 pathology-0.1.tar.gz
Và đây là một phân phối nhị phân:
$ python setup.py bdist_wheel running bdist_wheel running build running build_py creating build creating build/lib creating build/lib/pathology copying pathology/__init__.py -> build/lib/pathology copying pathology/path.py -> build/lib/pathology installing to build/bdist.macosx-10.7-x86_64/wheel running install running install_lib creating build/bdist.macosx-10.7-x86_64 creating build/bdist.macosx-10.7-x86_64/wheel creating build/bdist.macosx-10.7-x86_64/wheel/pathology copying build/lib/pathology/__init__.py -> build/bdist.macosx-10.7-x86_64/wheel/pathology copying build/lib/pathology/path.py -> build/bdist.macosx-10.7-x86_64/wheel/pathology running install_egg_info running egg_info writing pathology.egg-info/PKG-INFO writing dependency_links to pathology.egg-info/dependency_links.txt writing top-level names to pathology.egg-info/top_level.txt reading manifest file 'pathology.egg-info/SOURCES.txt' writing manifest file 'pathology.egg-info/SOURCES.txt' Copying pathology.egg-info to build/bdist.macosx-10.7-x86_64/wheel/pathology-0.1-py3.6.egg-info running install_scripts creating build/bdist.macosx-10.7-x86_64/wheel/pathology-0.1.dist-info/WHEEL
Pathology package chỉ có chứa các mô-đun Python thuần tuý, vì vậy một gói tổng quát có thể được xây dựng. Nếu gói của bạn có các phần mở rộng C, bạn sẽ phải xây dựng một wheel riêng cho mỗi nền tảng:
$ ls -la dist total 16 drwxr-xr-x 4 gigi.sayfan gigi.sayfan 136 Apr 18 21:24 . drwxr-xr-x 13 gigi.sayfan gigi.sayfan 442 Apr 18 21:24 .. -rw-r--r-- 1 gigi.sayfan gigi.sayfan 2695 Apr 18 21:24 pathology-0.1-py3-none-any.whl -rw-r--r-- 1 gigi.sayfan gigi.sayfan 1223 Apr 18 21:20 pathology-0.1.tar.gz
Để tìm hiểu sâu hơn về chủ đề đóng gói thư viện Python, hãy tham khảo bài viết Cách Viết các Gói Python của riêng bạn.
Cách Phân phối một gói Python
Python có một kho lưu trữ gói trung tâm được gọi là PyPI (Python Packages Index). Khi bạn cài đặt một gói Python bằng pip, nó sẽ tải gói từ PyPI (trừ khi bạn chỉ định một kho chứa khác). Để phân phối pathology package của chúng ta, chúng ta cần phải tải nó lên PyPI và cung cấp thêm một số matadata mà PyPI yêu cầu. Các bước là:
- Tạo một tài khoản trên PyPI (chỉ một lần).
- Đăng ký gói của bạn.
- Tải gói của bạn lên.
Tạo một Tài khoản
Bạn có thể tạo một tài khoản trên trang web PyPI. Sau đó tạo một tập tin .pypirc trong thư mục home của bạn:
[distutils] index-servers=pypi [pypi] repository = https://pypi.python.org/pypi username = the_gigi
Để thử, bạn có thể thêm một máy chủ chỉ mục "pypitest" vào tập tin .pypirc của bạn:
[distutils] index-servers= pypi pypitest [pypitest] repository = https://testpypi.python.org/pypi username = the_gigi [pypi] repository = https://pypi.python.org/pypi username = the_gigi
Đăng ký Gói của bạn
Nếu đây là lần xuất bản đầu tiên của gói, bạn cần phải đăng ký nó với PyPI. Sử dụng lệnh register của setup.py. Nó sẽ hỏi mật khẩu của bạn. Lưu ý rằng tôi trỏ nó đến kho thử nghiệm ở đây:
$ python setup.py register -r pypitest running register running egg_info writing pathology.egg-info/PKG-INFO writing dependency_links to pathology.egg-info/dependency_links.txt writing top-level names to pathology.egg-info/top_level.txt reading manifest file 'pathology.egg-info/SOURCES.txt' writing manifest file 'pathology.egg-info/SOURCES.txt' running check Password: Registering pathology to https://testpypi.python.org/pypi Server response (200): OK
Tải Gói của bạn lên
Bây giờ thì gói đã được đăng ký, chúng ta có thể tải nó lên. Tôi khuyên bạn nên sử dụng twine, là lựa chọn an toàn hơn. Cài đặt nó như bình thường bằng lệnh pip install twine
. Sau đó, tải gói của bạn lên bằng twine và cung cấp mật khẩu của bạn (chỉnh lại lệnh ở bên dưới):
$ twine upload -r pypitest -p <redacted> dist/* Uploading distributions to https://testpypi.python.org/pypi Uploading pathology-0.1-py3-none-any.whl [================================] 5679/5679 - 00:00:02 Uploading pathology-0.1.tar.gz [================================] 4185/4185 - 00:00:01
Để tìm hiểu sâu hơn về chủ đề phân phối gói, hãy tham khảo Cách Chia sẻ Gói Python của bạn.
Tóm tắt
Trong hướng dẫn này, chúng ta đã tìm hiểu quy trình đầy đủ trong việc viết một thư viện Python, đóng gói nó và phân phối nó thông qua PyPI. Tại thời điểm này, bạn sẽ có tất cả các công cụ để viết và chia sẻ thư viện của bạn với thế giới.
Ngoài ra, đừng ngại xem thử những gì chúng tôi đang có để bán và để nghiên cứu trên market và vui lòng hỏi bất kỳ câu hỏi nào và cung cấp phản hồi có giá trị của bạn bằng cách sử dụng phần bình luận bên dưới.
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Update me weeklyEnvato Tuts+ tutorials are translated into other languages by our community members—you can be involved too!
Translate this post