Đăng ký nhận thông tin về những video mới nhất
Tóm Tắt
Mục lục bài viết:
- Nhập Python cơ bản
- Mô-đun
- Các gói
- Nhập khẩu tuyệt đối và tương đối
- Đường dẫn nhập của Python
- Ví dụ: Cấu trúc nhập khẩu của bạn
- Tạo và cài đặt gói cục bộ
- Gói không gian tên
- Hướng dẫn kiểu nhập khẩu
- Nhập tài nguyên
- Giới thiệu importlib.resources
- Ví dụ: Sử dụng tệp dữ liệu
- Ví dụ: Thêm biểu tượng vào Tkinter GUIs
- Nhập động
- Sử dụng importlib
- Ví dụ: Phương pháp ban đầu với các gói không gian tên
- Ví dụ: Một gói các plugin
- Hệ thống nhập Python
- Nhập nội bộ
- Ví dụ: Singletons dưới dạng Mô-đun
- Đang tải lại các mô-đun
- Finders and Loaders
- Ví dụ: Tự động cài đặt từ PyPI
- Ví dụ: Nhập tệp dữ liệu
- Mẹo và thủ thuật nhập khẩu
- Xử lý các gói trên các phiên bản Python
- Xử lý các gói bị thiếu: Sử dụng một gói thay thế
- Xử lý các gói bị thiếu: Sử dụng Mock để thay thế
- Nhập tập lệnh dưới dạng mô-đun
- Chạy tập lệnh Python từ tệp ZIP
- Xử lý nhập khẩu theo chu kỳ
- Nhập hồ sơ
- Phần kết luận
Các bạn hoàn toàn có thể tải về code ví dụ tại ĐÂY để tiện trong quy trình khám phá bài viết này .
Trong Python, bạn sử dụng import
từ khóa để làm cho mã trong một mô-đun này có sẵn trong một mô-đun khác. Nhập bằng Python rất quan trọng để cấu trúc mã của bạn một cách hiệu quả. Sử dụng nhập đúng cách sẽ làm cho bạn năng suất hơn, cho phép bạn sử dụng lại mã trong khi vẫn giữ được các dự án của bạn.
Bạn đang đọc: Python: import: Các kỹ thuật và mẹo nâng cao
Hướng dẫn này sẽ cung cấp một cái nhìn tổng quan toàn diện về import
câu lệnh của Python và cách nó hoạt động. Hệ thống nhập rất mạnh và bạn sẽ học cách khai thác sức mạnh này. Trong khi bạn sẽ bao gồm nhiều khái niệm đằng sau hệ thống nhập của Python, hướng dẫn này chủ yếu là hướng dẫn ví dụ. Bạn sẽ học từ một số ví dụ mã trong suốt.
Trong hướng dẫn này, bạn sẽ học cách :
- Sử dụng mô-đun , gói và gói không gian tên
- Xử lý tài nguyên và tệp dữ liệu bên trong gói của bạn
- Nhập mô-đun động trong thời gian chạy
- Tùy chỉnh hệ thống nhập của Python
Python cơ bản import
Mã Python được tổ chức triển khai thành cả mô-đun và gói. Phần này sẽ lý giải chúng khác nhau như thế nào và bạn hoàn toàn có thể thao tác với chúng như thế nào .
Ở phần sau của hướng dẫn, bạn sẽ thấy 1 số ít cách sử dụng nâng cao và ít được biết đến của mạng lưới hệ thống nhập của Python. Tuy nhiên, hãy khởi đầu với những điều cơ bản : nhập mô-đun và gói .
Mô-đun
Các Python. org thuật ngữ định nghĩa mô-đun như sau :
Một đối tượng người tiêu dùng Giao hàng như một đơn vị chức năng tổ chức triển khai của mã Python. Mô-đun có một khoảng trống tên chứa những đối tượng người dùng Python tùy ý. Các mô-đun được tải vào Python bằng quy trình nhập. ( Nguồn )
Trong thực tế, một mô-đun thường tương ứng với một .py
tệp chứa mã Python.
Sức mạnh thực sự của những mô-đun là chúng hoàn toàn có thể được nhập và sử dụng lại trong mã khác. Hãy xem xét ví dụ sau :
>> >
>> >import math
>> >math.pi
3.141592653589793
Trong dòng đầu tiên import math
, bạn nhập mã trong math
mô-đun và làm cho nó có sẵn để sử dụng. Trong dòng thứ hai, bạn truy cập pi
biến trong math
mô-đun. math
là một phần của thư viện chuẩn của Python , có nghĩa là nó luôn có sẵn để nhập khi bạn đang chạy Python.
Lưu ý rằng bạn viết math.pi
và không chỉ đơn giản pi
. Ngoài việc là một mô-đun, math
hoạt động như một không gian tên giữ tất cả các thuộc tính của mô-đun lại với nhau. Không gian tên rất hữu ích để giữ cho mã của bạn dễ đọc và có tổ chức. Theo lời của Tim Peters:
Không gian tên là một trong những ý tưởng sáng tạo tuyệt vời — hãy làm nhiều hơn nữa ! ( Nguồn )
Bạn có thể liệt kê nội dung của một không gian tên với dir()
:
>> >
>> >import math
>> >dir( )
[ ' __annotations__ ', ' __builtins__ ', ..., ' math ' ]
>> >dir(math)
[ ' __doc__ ', ..., ' nan ', ' pi ', ' pow ', ... ]
Sử dụng dir()
mà không có bất kỳ đối số nào cho thấy những gì trong không gian tên chung. Để xem nội dung của math
không gian tên, bạn sử dụng dir(math)
.
Bạn đã thấy cách sử dụng đơn giản nhất của import
. Tuy nhiên, có những cách khác để sử dụng nó cho phép bạn nhập các phần cụ thể của mô-đun và đổi tên mô-đun khi bạn nhập.
Đoạn mã sau chỉ nhập pi
biến từ math
mô-đun:
>> >
>> >from math import pi
>> >pi
3.141592653589793
>> >math.pi
NameError : name ' math ' is not defined
Lưu ý rằng điều này pi
nằm trong không gian tên chung chứ không phải trong math
không gian tên.
Bạn cũng hoàn toàn có thể đổi tên những mô-đun và thuộc tính khi chúng được nhập :
>> >
>> >import math as m
>> >m.pi
3.141592653589793
>> >from math import pi as PI
>> >PI
3.141592653589793
Để biết thêm chi tiết cụ thể về cú pháp nhập mô-đun, hãy xem Mô-đun và gói Python – Giới thiệu .
Các gói
Bạn hoàn toàn có thể sử dụng một gói để tổ chức triển khai thêm những mô-đun của mình. Bảng chú giải thuật ngữ Python. org định nghĩa gói như sau :
Một mô-đun Python có thể chứa các mô-đun con hoặc đệ quy, các gói con. Về mặt kỹ thuật, một gói là một mô-đun Python có một
__path__
thuộc tính. ( Nguồn )
Lưu ý rằng một gói vẫn là một mô-đun. Là một người dùng, bạn thường không cần phải lo ngại về việc bạn đang nhập một mô-đun hay một gói .
Trong thực tế, một gói thường tương ứng với một thư mục tệp chứa tệp Python và các thư mục khác. Để tự tạo một gói Python, bạn tạo một thư mục và một tệp có tên__init__.py
bên trong nó. Các __init__.py
tập tin có chứa các nội dung của gói khi nó được coi là một mô-đun. Nó có thể được để trống.
Lưu ý: Các thư mục không có __init__.py
tệp vẫn được Python coi là gói. Tuy nhiên, đây không phải là các gói thông thường, mà là một thứ được gọi là các gói không gian tên . Bạn sẽ tìm hiểu thêm về chúng sau này .
Nói chung, các mô-đun con và gói con không được nhập khi bạn nhập một gói. Tuy nhiên, bạn có thể sử dụng __init__.py
để bao gồm bất kỳ hoặc tất cả các mô-đun con và gói con nếu bạn muốn. Để hiển thị một vài ví dụ về hành vi này, bạn sẽ tạo một gói để nói Hello world
bằng một vài ngôn ngữ khác nhau. Gói này sẽ bao gồm các thư mục và tệp sau:
world/
│
├── africa/
│ ├── __init__.py
│ └── zimbabwe.py
│
├── europe/
│ ├── __init__.py
│ ├── greece.py
│ ├── norway.py
│ └── spain.py
│
└── __init__.py
Mỗi tệp quốc gia sẽ in ra một lời chào, trong khi các __init__.py
tệp nhập một số gói con và mô-đun con một cách chọn lọc. Nội dung chính xác của các tệp như sau:
# world / africa / __init__. py ( Empty file )
# world / africa / zimbabwe.py
print(" Shona : Mhoroyi vhanu vese ")
print(" Ndebele : Sabona mhlaba ")
# world / europe / __init__. py
from. import greece
from. import norway
# world / europe / greece.py
print(" Greek : Γειά σας Κόσμε ")
# world / europe / norway.py
print(" Norwegian : Hei verden ")
# world / europe / spain.py
print(" Castellano : Hola mundo ")
# world / __init__. py
from. import africa
Lưu ý rằng world/__init__.py
chỉ nhập khẩu africa
và không nhập khẩu europe
. Tương tự, world/africa/__init__.py
không nhập bất cứ thứ gì, trong khi world/europe/__init__.py
nhập greece
và norway
nhưng không spain
. Mỗi mô-đun quốc gia sẽ in lời chào khi được nhập.
Hãy chơi với world
gói tại dấu nhắc tương tác để hiểu rõ hơn về cách các gói con và mô-đun con hoạt động:
>> >
>> >import world
>> >world
>> ># The africa subpackage has been automatically imported
>> >world.africa
>> ># The europe subpackage has not been imported
>> >world.europe
AttributeError : module ' world ' has no attribute ' europe '
Khi europe
được nhập, các mô-đun europe.greece
và europe.norway
mô-đun cũng được nhập. Bạn có thể thấy điều này vì các mô-đun quốc gia in lời chào khi chúng được nhập:
>> >
>> ># Import europe explicitly
>> >from world import europe
Greek : Γειά σας Κόσμε
Norwegian : Hei verden
>> ># The greece submodule has been automatically imported
>> >europe.greece
>> ># Because world is imported, europe is also found in the world namespace
>> >world.europe.norway
>> ># The spain submodule has not been imported
>> >europe.spain
AttributeError : module ' world.europe ' has no attribute ' spain '
>> ># Import spain explicitly inside the world namespace
>> >import world.europe.spain
Castellano : Hola mundo
>> ># Note that spain is also available directly inside the europe namespace
>> >europe.spain
>> ># Importing norway doesn't do the import again ( no output ), but adds
>> ># norway to the global namespace
>> >from world.europe import norway
>> >norway
Các world/africa/__init__.py
tập tin trống. Điều này có nghĩa là việc nhập world.africa
gói sẽ tạo ra không gian tên nhưng không có tác dụng nào khác:
>> >
>> ># Even though africa has been imported, zimbabwe has not
>> >world.africa.zimbabwe
AttributeError : module ' world.africa ' has no attribute ' zimbabwe '
>> ># Import zimbabwe explicitly into the global namespace
>> >from world.africa import zimbabwe
Shona : Mhoroyi vhanu vese
Ndebele : Sabona mhlaba
>> ># The zimbabwe submodule is now available
>> >zimbabwe
>> ># Note that zimbabwe can also be reached through the africa subpackage
>> >world.africa.zimbabwe
Hãy nhớ rằng, nhập một mô-đun vừa tải nội dung vừa tạo khoảng trống tên chứa nội dung. Một vài ví dụ sau cuối cho thấy rằng hoàn toàn có thể cùng một mô-đun là một phần của những khoảng trống tên khác nhau .
Chi tiết kỹ thuật: Không gian tên mô-đun được triển khai dưới dạng từ điển Python và có sẵn tại .__dict__
thuộc tính:
>> >
>> >import math
>> >math.__dict__[" pi "]
3.141592653589793
Bạn hiếm khi cần phải tương tác .__dict__
trực tiếp với .
Tương tự, không gian tên toàn cục của Python cũng là một từ điển. Bạn có thể truy cập nó thông qua globals()
.
Việc nhập các gói con và mô-đun con trong một __init__.py
tệp tin để làm cho chúng dễ sử dụng hơn cho người dùng của bạn là điều khá phổ biến . Bạn có thể xem một ví dụ về điều này trong requests
gói phổ biến .
Nhập khẩu tuyệt đối và tương đối
Nhớ lại mã nguồn của world/__init__.py
trong ví dụ trước đó:
from. import africa
Bạn đã từng thấy những from...import
câu như vậy from math import pi
, nhưng dấu chấm ( .
) from. import africa
có nghĩa là gì?
Dấu chấm đề cập đến gói hiện tại và câu lệnh là một ví dụ về nhập tương đối . Bạn có thể đọc nó là “Từ gói hiện tại, hãy nhập gói con africa
”.
Có một câu lệnh nhập tuyệt đối tương tự trong đó bạn đặt tên rõ ràng cho gói hiện tại :
from world import africa
Trên thực tế, tất cả các hoạt động nhập khẩu vào world
có thể đã được thực hiện một cách rõ ràng với các nhập khẩu tuyệt đối tương tự.
Nhập tương đối phải ở dạng from...import
và vị trí bạn đang nhập phải bắt đầu bằng dấu chấm.
Các phong thái dẫn PEP 8 khuyến nghị sử dụng nhập khẩu tuyệt đối nói chung. Tuy nhiên, nhập khẩu tương đối là một giải pháp thay thế sửa chữa để tổ chức triển khai phân cấp gói. Để biết thêm thông tin, hãy xem Nhập tuyệt đối so với Nhập tương đối bằng Python .
Đường dẫn nhập của Python
Làm cách nào để Python tìm thấy những mô-đun và những gói mà nó nhập vào ? Bạn sẽ thấy thêm cụ thể về chính sách của mạng lưới hệ thống nhập Python sau. Hiện tại, chỉ cần biết rằng Python tìm kiếm những mô-đun và gói trong đường dẫn nhập của nó. Đây là list những vị trí được tìm kiếm những mô-đun để nhập .
Lưu ý: Khi bạn nhập import something
, Python sẽ tìm kiếm something
một vài vị trí khác nhau trước khi tìm kiếm đường dẫn nhập.
Đặc biệt, nó sẽ tìm kiếm trong bộ nhớ cache của mô-đun để xem liệu something
đã được nhập chưa và nó sẽ tìm kiếm trong số các mô-đun được tích hợp sẵn.
Bạn sẽ khám phá thêm về máy móc nhập Python vừa đủ trong phần sau .
Bạn có thể kiểm tra đường dẫn nhập của Python bằng cách in sys.path
. Nói chung, danh sách này sẽ chứa ba loại địa điểm khác nhau:
- Thư mục của tập lệnh hiện tại (hoặc thư mục hiện tại nếu không có tập lệnh nào, chẳng hạn như khi Python đang chạy tương tác)
- Nội dung của
PYTHONPATH
biến môi trường - Các thư mục khác, phụ thuộc vào cài đặt
Thông thường, Python sẽ khởi đầu ở đầu list những vị trí và tìm kiếm một mô-đun nhất định trong mỗi vị trí cho đến khi khớp tiên phong. Vì thư mục tập lệnh hoặc thư mục hiện tại luôn nằm tiên phong trong list này, bạn hoàn toàn có thể bảo vệ rằng những tập lệnh của mình tìm thấy những mô-đun và gói tự tạo của bạn bằng cách tổ chức triển khai những thư mục của bạn và cẩn trọng về việc bạn chạy Python từ thư mục nào .
Tuy nhiên, bạn cũng nên cẩn thận rằng bạn không tạo các mô-đun làm bóng hoặc ẩn các mô-đun quan trọng khác. Ví dụ, giả sử rằng bạn xác định math
mô-đun sau :
# math.py
def double(number) :
return 2 * number
Sử dụng mô-đun này hoạt động giải trí như mong đợi :
>> >
>> >import math
>> >math.double(3.14)
6.28
Nhưng mô-đun này cũng che bóng math
mô-đun được bao gồm trong thư viện tiêu chuẩn. Thật không may, điều đó có nghĩa là ví dụ trước đó của chúng tôi về việc tìm kiếm giá trị của π không còn hoạt động nữa:
>> >
>> >import math
>> >math.pi
Traceback ( most recent call last ) :
File ""
, line 1, in
AttributeError: module ' math ' has no attribute ' pi '
>> >math
Vấn đề là Python hiện tìm kiếm math
mô-đun mới của bạn pi
thay vì tìm kiếm math
mô-đun trong thư viện chuẩn.
Để tránh những loại vấn đề này, bạn nên cẩn thận với tên của các mô-đun và gói của mình. Đặc biệt, tên gói và mô-đun cấp cao nhất của bạn phải là duy nhất. Nếu math
được định nghĩa là một mô-đun con trong một gói, thì nó sẽ không phủ bóng mô-đun tích hợp sẵn.
Ví dụ: Cấu trúc nhập khẩu của bạn
Mặc dù bạn có thể sắp xếp việc nhập bằng cách sử dụng thư mục hiện tại cũng như bằng cách thao tác PYTHONPATH
và thậm chí sys.path
, quá trình này thường không cầu kỳ và dễ xảy ra lỗi. Để xem một ví dụ điển hình, hãy xem xét ứng dụng sau:
structure/
│
├── files.py
└── structure.py
Ứng dụng sẽ tạo lại cấu trúc tệp nhất định bằng cách tạo thư mục và tệp trống. Các structure.py
tập tin có chứa các kịch bản chính, và files.py
là một module thư viện với một vài chức năng để đối phó với các tập tin. Sau đây là một ví dụ về kết xuất từ ứng dụng, trong trường hợp này là chạy nó trong structure
thư mục:
USDpython structure.py .
Create file : / home / gahjelle / structure / 001 / structure.py
Create file : / home / gahjelle / structure / 001 / files.py
Create file : / home / gahjelle / structure / 001 / __pycache__ / files.cpython-38.pyc
Hai tệp mã nguồn cũng như .pyc
tệp được tạo tự động được tạo lại bên trong một thư mục mới có tên 001
.
Bây giờ hãy xem mã nguồn. Chức năng chính của ứng dụng được xác định trong structure.py
:
1# structure / structure.py
2
3# Standard library imports
4import pathlib
5import sys
6
7# Local imports
8import files
9
10def main( ) :
11 # Read path from command line
12 try:
13 root = pathlib.Path(sys.argv[1] ).resolve( )
14 except IndexError:
15 print(" Need one argument : the root of the original file tree ")
16 raise SystemExit( )
17
18 # Re-create the file structure
19 new_root = files.unique_path(pathlib.Path.cwd( ), "{ : 03 d }")
20 for path in root.rglob(" * ") :
21 if path.is_file( ) and new_root not in path.parents:
22 rel_path = path.relative_to(root)
23 files.add_empty_file(new_root / rel_path)
24
25if __name__ = = " __main__ ":
26 main( )
Trong các dòng từ 12 đến 16 , bạn đọc một đường dẫn gốc từ dòng lệnh. Trong ví dụ trên, bạn sử dụng dấu chấm, có nghĩa là thư mục hiện tại. Đường dẫn này sẽ được sử dụng làm root
phân cấp tệp mà bạn sẽ tạo lại.
Công việc thực tế xảy ra từ dòng 19 đến dòng 23 . Đầu tiên, bạn tạo một đường dẫn duy nhất new_root
, đó sẽ là đường dẫn gốc của hệ thống phân cấp tệp mới của bạn. Sau đó, bạn lặp qua tất cả các đường dẫn bên dưới bản gốc root
và tạo lại chúng dưới dạng tệp trống bên trong hệ thống phân cấp tệp mới.
Đối với thao tác với các đường dẫn như thế này, pathlib
trong thư viện tiêu chuẩn là khá hữu ích. Để biết thêm chi tiết về cách nó được sử dụng, hãy xem Mô-đun của Python 3 pathlib
: Điều chỉnh hệ thống tệp .
Ở dòng 26 , bạn gọi main()
. Bạn sẽ tìm hiểu thêm về if
bài kiểm tra ở dòng 25 sau . Hiện tại, bạn nên biết rằng biến đặc biệt __name__
có giá trị __main__
bên trong các tập lệnh, nhưng nó nhận tên của mô-đun bên trong các mô-đun được nhập. Để biết thêm thông tin __name__
, hãy xem Định nghĩa các hàm chính trong Python .
Lưu ý rằng bạn nhập files
trên dòng 8 . Mô-đun thư viện này chứa hai chức năng tiện ích:
# structure / files.py
def unique_path(directory, name_pattern) :
" " " Find a path name that does not already exist " " "
counter = 0
while True:
counter + = 1
path = directory / name_pattern.format(counter)
if not path.exists( ) :
return path
def add_empty_file(path) :
" " " Create an empty file at the given path " " "
print(f" Create file :{path}")
path.parent.mkdir(parents=True, exist_ok=True)
path.touch( )
unique_path()
sử dụng bộ đếm để tìm đường dẫn chưa tồn tại. Trong ứng dụng, bạn sử dụng nó để tìm một thư mục con duy nhất để sử dụng làm new_root
hệ thống phân cấp tệp được tạo lại. Tiếp theo, add_empty_file()
hãy đảm bảo rằng tất cả các thư mục cần thiết đã được tạo trước khi tạo một tệp trống bằng cách sử dụng .touch()
.
Hãy xem xét việc nhập files
lại:
7# Local imports
8import files
Nó trông khá ngây thơ. Tuy nhiên, khi dự án phát triển, dòng này sẽ khiến bạn đau đầu. Ngay cả khi bạn nhập files
từ structure
dự án, quá trình nhập là tuyệt đối : nó không bắt đầu bằng dấu chấm. Điều này có nghĩa là files
phải được tìm thấy trong đường dẫn nhập để nhập hoạt động.
May mắn thay, thư mục chứa tập lệnh hiện tại luôn nằm trong đường dẫn nhập của Python, thế cho nên điều này hiện hoạt động giải trí tốt. Tuy nhiên, nếu dự án Bất Động Sản của bạn đạt được 1 số ít lực kéo, thì nó hoàn toàn có thể được sử dụng theo những cách khác .
Ví dụ: một người nào đó có thể muốn nhập tập lệnh vào một Máy tính xách tay Jupyter và chạy nó từ đó. Hoặc họ có thể muốn sử dụng lại files
thư viện trong một dự án khác. Họ thậm chí có thể tạo tệp thực thi bằng PyInstaller để dễ dàng phân phối tệp đó hơn. Thật không may, bất kỳ trường hợp nào trong số này đều có thể tạo ra sự cố với việc nhập files
.
Để xem ví dụ, bạn hoàn toàn có thể làm theo hướng dẫn PyInstaller và tạo một điểm vào ứng dụng của mình. Thêm một thư mục bổ trợ bên ngoài thư mục ứng dụng của bạn :
structure/
│
├── structure/
│ ├── files.py
│ └── structure.py
│
└── cli.py
Trong thư mục bên ngoài, tạo ra các kịch bản điểm nhập cảnh, cli.py
:
# cli.py
from structure.structure import main
if __name__ = = " __main__ ":
main( )
Tập lệnh này sẽ nhập main()
từ tập lệnh gốc của bạn và chạy nó. Lưu ý rằng main()
không chạy khi structure
được nhập vì if
thử nghiệm trên dòng 25 in structure.py
. Điều đó có nghĩa là bạn cần chạy main()
một cách rõ ràng.
Về kim chỉ nan, điều này sẽ hoạt động giải trí tương tự như như chạy ứng dụng trực tiếp :
USDpython cli.py structure
Traceback ( most recent call last ) :
File "cli.py", line 1, in
from structure.structure import main
File "/home/gahjelle/structure/structure/structure.py", line 8, in
import files
ModuleNotFoundError : No module named ' files '
Tại sao điều đó không hoạt động? Đột nhiên, quá trình nhập files
phát sinh lỗi.
Vấn đề là bằng cách khởi động ứng dụng bằng cli.py
, bạn đã thay đổi vị trí của tập lệnh hiện tại, từ đó thay đổi đường dẫn nhập. files
không còn trên đường dẫn nhập nữa, vì vậy không thể nhập tuyệt đối.
Một giải pháp khả thi là biến hóa đường dẫn nhập của Python :
7# Local imports
8sys.path.insert(0, str(pathlib.Path(__file__).parent) )
9import files
Điều này hoạt động vì đường dẫn nhập bao gồm thư mục chứa structure.py
và files.py
. Vấn đề với cách tiếp cận này là đường dẫn nhập của bạn có thể rất lộn xộn và khó hiểu.
Trong trong thực tiễn, bạn đang tạo lại một tính năng của những phiên bản Python tiên phong được gọi là nhập tương đối ngầm định. Những điều này đã bị vô hiệu khỏi ngôn từ bởi PEP 328 với nguyên do sau :
Trong Python 2.4 trở về trước, nếu bạn đang đọc một mô-đun nằm bên trong một gói, thì không rõ liệu có
import foo
tham chiếu đến mô-đun cấp cao nhất hay đến một mô-đun khác bên trong gói hay không. Khi thư viện của Python mở rộng, ngày càng có nhiều mô-đun bên trong gói hiện có đột nhiên làm bóng các mô-đun thư viện tiêu chuẩn một cách tình cờ. Đó là một vấn đề đặc biệt khó khăn bên trong các gói vì không có cách nào để chỉ định mô-đun nào là nghĩa của nó. ( Nguồn )
Một giải pháp khác là sử dụng nhập tương đối để thay thế. Thay đổi nhập structure.py
như sau:
7# Local imports
8from. import files
Bây giờ bạn hoàn toàn có thể khởi động ứng dụng của mình trải qua tập lệnh điểm nhập :
USDpython cli.py structure
Create file : / home / gahjelle / structure / 001 / structure.py
Create file : / home / gahjelle / structure / 001 / files.py
Create file : / home / gahjelle / structure / 001 / __pycache__ / structure.cpython-38.pyc
Create file : / home / gahjelle / structure / 001 / __pycache__ / files.cpython-38.pyc
Rất tiếc, bạn không hề gọi ứng dụng trực tiếp được nữa :
USDpython structure.py .
Traceback ( most recent call last ) :
File "structure.py", line 8, in
from. import files
ImportError : cannot import name ' files ' from ' __main__ ' ( structure.py )
Vấn đề là việc nhập tương đối được giải quyết trong các tập lệnh khác với các mô-đun được nhập. Tất nhiên, bạn có thể quay lại và khôi phục quá trình nhập tuyệt đối trước khi chạy trực tiếp tập lệnh hoặc thậm chí bạn có thể thực hiện một số động tác try...except
nhào lộn để nhập tệp hoàn toàn hoặc tương đối tùy thuộc vào những gì hoạt động.
Thậm chí còn có một vụ hack được chính thức xử phạt để làm cho việc nhập tương đối hoạt động trong các tập lệnh. Thật không may, điều này cũng buộc bạn phải thay đổi sys.path
trong hầu hết các trường hợp. Trích lời Raymond Hettinger :
Phải có cách tốt hơn ! ( Nguồn )
Thật vậy, một giải pháp tốt hơn — và ổn định hơn — là chơi cùng với hệ thống nhập và đóng gói của Python và cài đặt dự án của bạn dưới dạng gói cục bộ bằng cách sử dụngpip
.
Tạo và cài đặt gói cục bộ
Khi bạn setup một gói từ PyPI, gói đó có sẵn cho tổng thể những tập lệnh trong thiên nhiên và môi trường của bạn. Tuy nhiên, bạn cũng hoàn toàn có thể thiết lập những gói từ máy tính cục bộ của mình và chúng cũng sẽ được cung ứng theo cách tương tự như .
Tạo một gói cục bộ không liên quan nhiều đến chi phí. Đầu tiên, tạo tối thiểu setup.cfg
và setup.py
các tệp trong structure
thư mục bên ngoài :
# setup.cfg
[metadata]
name = local_structure
version = 0.1.0
[options]
packages = structure
# setup.py
import setuptools
setuptools.setup( )
Về lý thuyết, cái name
và version
có thể là bất cứ thứ gì bạn thích. Tuy nhiên, chúng sẽ được sử dụng pip
khi tham chiếu đến gói của bạn, vì vậy bạn nên chọn các giá trị dễ nhận biết và không đụng chạm với các gói khác mà bạn sử dụng.
Một mẹo là cung cấp cho tất cả các gói cục bộ như vậy một tiền tố chung như local_
hoặc tên người dùng của bạn. packages
nên liệt kê thư mục hoặc các thư mục chứa mã nguồn của bạn. Sau đó, bạn có thể cài đặt gói cục bộ bằng cách sử dụng pip
:
USDpython -m pip install -e .
Lệnh này sẽ cài đặt gói vào hệ thống của bạn. structure
sau đó sẽ được tìm thấy trên đường dẫn nhập của Python, có nghĩa là bạn có thể sử dụng nó ở bất cứ đâu mà không phải lo lắng về thư mục tập lệnh, nhập tương đối hoặc các biến chứng khác. Các -e
tùy chọn là viết tắt của có thể chỉnh sửa , đó là quan trọng vì nó cho phép bạn thay đổi mã nguồn của gói của bạn mà không cần cài đặt lại nó.
Lưu ý : Loại tệp thiết lập này hoạt động giải trí tốt khi bạn đang thao tác với những dự án Bất Động Sản của riêng mình. Tuy nhiên, nếu bạn định san sẻ mã với người khác, thì bạn nên thêm 1 số ít thông tin vào tệp thiết lập của mình .
Để biết thêm cụ thể về những tệp thiết lập, hãy xem Cách xuất bản Gói Python nguồn mở lên PyPI .
Bây giờ nó đã structure
được cài đặt trên hệ thống của bạn, bạn có thể sử dụng câu lệnh nhập sau:
7# Local imports
8from structure import files
Điều này sẽ hoạt động giải trí bất kể bạn kết thúc cuộc gọi ứng dụng của mình như thế nào .
Mẹo : Trong mã của riêng bạn, bạn nên tách những tập lệnh và thư viện một cách có ý thức. Đây là một nguyên tắc nhỏ :
- Một tập lệnh được dùng để chạy.
- Một thư viện có nghĩa là được nhập.
Bạn hoàn toàn có thể có mã mà bạn muốn vừa chạy riêng vừa nhập từ những tập lệnh khác. Trong trường hợp đó, thường nên cấu trúc lại mã của bạn để bạn chia phần chung thành một mô-đun thư viện .
Mặc dù tách những tập lệnh và thư viện là một sáng tạo độc đáo hay, nhưng toàn bộ những tệp Python đều hoàn toàn có thể được thực thi và nhập. Trong phần sau, bạn sẽ tìm hiểu và khám phá thêm về cách tạo những mô-đun giải quyết và xử lý tốt cả hai .
Gói không gian tên
Các mô-đun và gói Python có tương quan rất ngặt nghèo đến những tệp và thư mục. Điều này làm cho Python độc lạ với nhiều ngôn từ lập trình khác, trong đó những gói chỉ hoạt động giải trí như khoảng trống tên mà không thực thi cách tổ chức triển khai mã nguồn. Xem bàn luận trong PEP 402 để biết những ví dụ .
Các gói không gian tên đã có sẵn bằng Python kể từ phiên bản 3.3. Chúng ít phụ thuộc hơn vào hệ thống phân cấp tệp cơ bản. Đặc biệt, các gói không gian tên có thể được chia thành nhiều thư mục. Gói không gian tên được tạo tự động nếu bạn có thư mục chứa .py
tệp nhưng không có __init__.py
. Xem PEP 420 để biết giải thích chi tiết.
Lưu ý : Nói một cách đúng chuẩn, những gói khoảng trống tên ngầm định đã được trình làng trong Python 3.3. Trong những phiên bản Python trước, bạn hoàn toàn có thể tạo những gói vùng tên theo cách thủ công bằng tay theo một số ít cách không thích hợp khác nhau. PEP 420 thống nhất và đơn giản hóa những chiêu thức tiếp cận trước đó .
Để hiểu rõ hơn về lý do tại sao các gói không gian tên có thể hữu ích, chúng ta hãy thử triển khai một gói. Như một ví dụ thúc đẩy, bạn sẽ có một bước đi khác về vấn đề được giải quyết trong Mô hình phương pháp nhà máy và Triển khai của nó bằng Python : với một Song
đối tượng, bạn muốn chuyển đổi nó thành một trong số các biểu diễn chuỗi. Nói cách khác, bạn muốn tuần tự hóa Song
các đối tượng.
Để đơn cử hơn, bạn muốn tiến hành mã hoạt động giải trí như sau :
>> >
>> >tuy nhiên = Song(song_id=" 1 ", title=" The Same River ", artist=" Riverside ")
>> >tuy nhiên.serialize( )
' { " id " : " 1 ", " title " : " The Same River ", " artist " : " Riverside " } '
Hãy giả sử rằng bạn như mong muốn và phát hiện việc tiến hành của bên thứ ba một số ít định dạng mà bạn cần tuần tự hóa tới và nó được tổ chức triển khai như một gói khoảng trống tên :
third_party/
│
└── serializers/
├── json.py
└── xml.py
Tệp json.py
chứa mã có thể tuần tự hóa một đối tượng sang định dạng JSON :
# third_party / serializers / json.py
import json
class JsonSerializer:
def __init__(self) :
self._current_object = None
def start_object(self, object_name, object_id) :
self._current_object = dict(id=object_id)
def add_property(self, name, value) :
self._current_object[name] = value
def __str__(self) :
return json.dumps(self._current_object)
Giao diện serializer này có một chút ít hạn chế, nhưng nó sẽ đủ để chứng tỏ cách hoạt động giải trí của những gói khoảng trống tên .
Tệp xml.py
chứa một tệp tương tự XmlSerializer
có thể chuyển đổi một đối tượng sang XML :
# third_party / serializers / xml.py
import xml.etree. ElementTree as et
class XmlSerializer:
def __init__(self) :
self._element = None
def start_object(self, object_name, object_id) :
self._element = et.Element(object_name, attrib={" id ": object_id} )
def add_property(self, name, value) :
prop = et.SubElement(self._element, name)
prop.text = value
def __str__(self) :
return et.tostring(self._element, encoding=" unicode ")
Lưu ý rằng cả hai lớp thực hiện cùng một giao diện với .start_object()
, .add_property()
và .__str__()
phương pháp.
Sau đó, bạn tạo một Song
lớp có thể sử dụng các trình tuần tự này:
# song.py
class Song:
def __init__(self, song_id, title, artist) :
self.song_id = song_id
self.title = title
self.artist = artist
def serialize(self, serializer) :
serializer.start_object(" tuy nhiên ", self.song_id)
serializer.add_property(" title ", self.title)
serializer.add_property(" artist ", self.artist)
return str(serializer)
A Song
được xác định bởi ID, chức danh và nghệ sĩ của nó. Lưu ý rằng .serialize()
không cần biết nó chuyển đổi sang định dạng nào vì nó sử dụng giao diện chung được xác định trước đó.
Giả sử rằng bạn đã cài đặt serializers
gói của bên thứ ba , bạn có thể sử dụng nó như sau:
>> >
>> >from serializers.json import JsonSerializer
>> >from serializers.xml import XmlSerializer
>> >from tuy nhiên import Song
>> >tuy nhiên = Song(song_id=" 1 ", title=" The Same River ", artist=" Riverside ")
>> >tuy nhiên.serialize(JsonSerializer( ) )
' { " id " : " 1 ", " title " : " The Same River ", " artist " : " Riverside " } '
>> >tuy nhiên.serialize(XmlSerializer( ) )
'The Same River Riverside '
Bằng cách cung cấp các đối tượng serializer khác nhau .serialize()
, bạn sẽ có được các bản trình bày khác nhau cho bài hát của mình.
Lưu ý: Bạn có thể nhận được một ModuleNotFoundError
hoặc một ImportError
khi tự chạy mã. Điều này là do serializers
không có trong đường dẫn nhập Python của bạn . Bạn sẽ sớm thấy cách giải quyết vấn đề đó.
Càng xa càng tốt. Tuy nhiên, bây giờ bạn nhận ra rằng bạn cũng cần phải chuyển đổi các bài hát của mình thành biểu diễn YAML , không được hỗ trợ trong thư viện của bên thứ ba. Nhập sự kỳ diệu của các gói không gian tên: bạn có thể thêm gói của riêng mình YamlSerializer
vào serializers
gói mà không cần chạm vào thư viện của bên thứ ba.
Đầu tiên, tạo một thư mục trên hệ thống tệp cục bộ của bạn có tên serializers
. Điều quan trọng là tên của thư mục phải khớp với tên của gói không gian tên mà bạn đang tùy chỉnh:
local/
│
└── serializers/
└── yaml.py
Trong yaml.py
tệp, bạn xác định của riêng bạn YamlSerializer
. Bạn căn cứ vào PyYAML
gói này , gói phải được cài đặt từ PyPI:
USDpython -m pip install PyYAML
Vì YAML và JSON là các định dạng khá giống nhau, bạn có thể sử dụng lại hầu hết việc triển khai JsonSerializer
:
# local / serializers / yaml.py
import yaml
from serializers.json import JsonSerializer
class YamlSerializer(JsonSerializer) :
def __str__(self) :
return yaml.dump(self._current_object)
Lưu ý rằng dấu YamlSerializer
dựa trên JsonSerializer
, được nhập từ serializers
chính nó. Kể từ khi cả hai json
và yaml
là một phần của gói namespace cùng, thậm chí bạn có thể sử dụng một khẩu tương đối: from .json import JsonSerializer
.
Tiếp tục ví dụ trên, giờ đây bạn cũng hoàn toàn có thể quy đổi bài hát sang YAML :
>> >
>> >from serializers.yaml import YamlSerializer
>> >tuy nhiên.serialize(YamlSerializer( ) )
" artist : Riverside \ nid : ' 1 ' \ ntitle : The Same River \ n "
Cũng giống như các mô-đun và gói thông thường, các gói không gian tên phải được tìm thấy trên đường dẫn nhập Python. Nếu bạn đang làm theo cùng với các ví dụ trước, thì có thể bạn đã gặp vấn đề với việc không tìm thấy Python serializers
. Trong mã thực tế, bạn đã sử dụng pip
để cài đặt thư viện của bên thứ ba, vì vậy nó sẽ tự động nằm trong đường dẫn của bạn.
Lưu ý : Trong ví dụ bắt đầu, việc lựa chọn bộ nối tiếp được triển khai linh động hơn. Bạn sẽ thấy cách sử dụng những gói khoảng trống tên trong một mẫu chiêu thức gốc thích hợp sau này .
Bạn cũng nên đảm bảo rằng thư viện cục bộ của bạn có sẵn giống như một gói thông thường. Như đã giải thích ở trên, bạn có thể thực hiện việc này bằng cách chạy Python từ thư mục thích hợp hoặc bằng cách sử dụng pip
để cài đặt thư viện cục bộ.
Trong ví dụ này, bạn đang thử nghiệm cách tích hợp gói bên thứ ba giả mạo với gói cục bộ của mình. Nếu third_party
là một gói thực, thì bạn sẽ tải xuống từ PyPI bằng cách sử dụng pip
. Vì điều này là không thể, bạn có thể mô phỏng nó bằng cách cài đặt third_party
cục bộ giống như bạn đã làm trong structure
ví dụ trước đó .
Ngoài ra, bạn có thể làm rối với đường dẫn nhập của mình. Đặt third_party
và local
các thư mục bên trong cùng một thư mục, sau đó tùy chỉnh đường dẫn Python của bạn như sau:
>> >
>> >import sys
>> >sys.path.extend( [" third_party ", " local "] )
>> >from serializers import json, xml, yaml
>> >json
>> >yaml
Giờ đây, bạn hoàn toàn có thể sử dụng tổng thể những bộ tuần tự hóa mà không cần lo ngại về việc chúng được xác lập trong gói của bên thứ ba hay cục bộ .
Hướng dẫn kiểu nhập khẩu
PEP 8, hướng dẫn kiểu Python, có 1 số ít khuyến nghị về việc nhập. Như mọi khi với Python, giữ cho mã của bạn vừa hoàn toàn có thể đọc được vừa hoàn toàn có thể bảo dưỡng là một điều quan trọng cần xem xét. Dưới đây là một số ít quy tắc chung về cách tạo kiểu cho những mục nhập của bạn :
- Giữ các mục nhập ở đầu tệp.
- Viết nhập khẩu trên các dòng riêng biệt.
- Sắp xếp quá trình nhập thành các nhóm: nhập thư viện tiêu chuẩn đầu tiên, sau đó nhập khẩu của bên thứ ba và cuối cùng là nhập ứng dụng hoặc thư viện cục bộ.
- Đặt hàng nhập khẩu theo thứ tự bảng chữ cái trong mỗi nhóm.
- Ưu tiên nhập khẩu tuyệt đối hơn nhập khẩu tương đối.
- Tránh nhập ký tự đại diện như
from module import *
.
isort
và reorder-python-imports
là những công cụ tuyệt vời để thực thi một phong cách nhất quán cho các mục nhập của bạn.
Dưới đây là ví dụ về phần nhập bên trong gói trình đọc nguồn cấp tài liệu Python thực :
# Standard library imports
import sys
from typing import Dict, List
# Third party imports
import feedparser
import html2text
# Reader imports
from reader import URL
Lưu ý cách nhóm này làm cho các phụ thuộc của mô-đun này rõ ràng: feedparser
và html2text
cần được cài đặt trên hệ thống. Nhìn chung, bạn có thể cho rằng thư viện tiêu chuẩn có sẵn. Việc tách các mục nhập từ bên trong gói của bạn cung cấp cho bạn một số tổng quan về các phần phụ thuộc nội bộ của mã của bạn.
Có những trường hợp nên uốn cong những quy tắc này một chút ít. Bạn đã thấy rằng nhập khẩu tương đối hoàn toàn có thể là một giải pháp sửa chữa thay thế cho việc tổ chức triển khai phân cấp gói. Sau đó, bạn sẽ thấy cách trong 1 số ít trường hợp, bạn hoàn toàn có thể chuyển nhập vào một định nghĩa hàm để phá vỡ những chu kỳ luân hồi nhập .
Nhập tài nguyên
Đôi khi bạn sẽ có mã nhờ vào vào tệp tài liệu hoặc những tài nguyên khác. Trong những tập lệnh nhỏ, đây không phải là yếu tố — bạn hoàn toàn có thể chỉ định đường dẫn đến tệp tài liệu của mình và liên tục !
Tuy nhiên, nếu tệp tài nguyên quan trọng so với gói của bạn và bạn muốn phân phối gói của mình cho những người dùng khác, thì 1 số ít thử thách sẽ phát sinh :
-
Bạn sẽ không có quyền kiểm soát đường dẫn đến tài nguyên vì điều đó sẽ phụ thuộc vào thiết lập của người dùng của bạn cũng như cách gói được phân phối và cài đặt. Bạn có thể cố gắng tìm ra đường dẫn tài nguyên dựa trên gói
__file__
hoặc__path__
thuộc tính của bạn, nhưng điều này có thể không phải lúc nào cũng hoạt động như mong đợi. -
Gói của bạn có thể cư trú bên trong một file ZIP hoặc một tuổi
.egg
tập tin , trong trường hợp tài nguyên thậm chí sẽ không thể là một tập tin vật lý trên hệ thống của người dùng.
Đã có một số nỗ lực để giải quyết những thách thức này, bao gồm cả setuptools.pkg_resources
. Tuy nhiên, với việc đưa importlib.resources
vào thư viện tiêu chuẩn trong Python 3.7 , bây giờ có một cách tiêu chuẩn để xử lý các tệp tài nguyên.
Giới thiệu importlib.resources
importlib.resources
cấp quyền truy cập vào các tài nguyên trong các gói. Trong ngữ cảnh này, tài nguyên là bất kỳ tệp nào nằm trong một gói có thể nhập. Tệp có thể có hoặc không tương ứng với tệp vật lý trên hệ thống tệp.
Điều này có một vài lợi thế. Bằng cách sử dụng lại mạng lưới hệ thống nhập, bạn sẽ có cách giải quyết và xử lý những tệp bên trong những gói của mình một cách đồng điệu hơn. Nó cũng được cho phép bạn truy vấn thuận tiện hơn vào những tệp tài nguyên trong những gói khác. Tài liệu tóm tắt nó một cách độc lạ :
Nếu bạn hoàn toàn có thể nhập một gói, bạn hoàn toàn có thể truy vấn tài nguyên trong gói đó. ( Nguồn )
importlib.resources
đã trở thành một phần của thư viện chuẩn trong Python 3.7. Tuy nhiên, trên các phiên bản cũ hơn của Python, một cổng hỗ trợ có sẵn dưới dạngimportlib_resources
. Để sử dụng backport, hãy cài đặt nó từ PyPI :
USDpython -m pip install importlib_resources
Backport thích hợp với Python 2.7 cũng như Python 3.4 và những phiên bản mới hơn .
Có một yêu cầu khi sử dụng importlib.resources
: các tệp tài nguyên của bạn phải có sẵn bên trong một gói thông thường. Gói không gian tên không được hỗ trợ. Trong thực tế, điều này có nghĩa là tệp phải nằm trong thư mục chứa __init__.py
tệp.
Ví dụ tiên phong, giả sử bạn có tài nguyên bên trong một gói như thế này :
books/
│
├── __init__.py
├── alice_in_wonderland.png
└── alice_in_wonderland.txt
__init__.py
chỉ là một tệp trống cần thiết để chỉ định books
là một gói thông thường.
Sau đó, bạn có thể sử dụng open_text()
và open_binary()
để mở các tệp văn bản và tệp nhị phân, tương ứng:
>> >
>> >from importlib import resources
>> >with resources.open_text(" books ", " alice_in_wonderland. txt ") as fid:
... alice = fid.readlines( )
...
>> >print(" ".join(alice[ :7] ) )
CHAPTER I. Down the Rabbit-Hole
Alice was beginning to get very tired of sitting by her sister on the
ngân hàng, and of having nothing to do : once or twice she had peeped into the
book her sister was reading, but it had no pictures or conversations in
it, ' and what is the use of a book, ' thought Alice ' without pictures or
conversations ? '
>> >with resources.open_binary(" books ", " alice_in_wonderland. png ") as fid:
... cover = fid.read( )
...
>> >cover[ :8] # PNG file signature
b ' \ x89PNG \ r \ n \ x1a \ n '
open_text()
và open_binary()
tương đương với built-in open()
với các mode
tham số thiết lập để rt
và rb
, tương ứng. Các chức năng thuận tiện để đọc trực tiếp văn bản hoặc tệp nhị phân cũng có sẵn như read_text()
và read_binary()
. Xem tài liệu chính thức để biết thêm thông tin.
Lưu ý: Để sử dụng backport một cách liền mạch trên các phiên bản Python cũ hơn, bạn có thể nhập importlib.resources
như sau:
try:
from importlib import resources
except ImportError:
import importlib_resources as resources
Xem phần mẹo và thủ pháp của hướng dẫn này để biết thêm thông tin .
Phần còn lại của phần này sẽ trình diễn một vài ví dụ phức tạp về việc sử dụng tệp tài nguyên trong trong thực tiễn .
Ví dụ: Sử dụng tệp dữ liệu
Là một ví dụ đầy đủ hơn về việc sử dụng tệp dữ liệu, bạn sẽ thấy cách triển khai chương trình đố vui dựa trên dữ liệu dân số của Liên hợp quốc . Đầu tiên, hãy tạo một data
gói và tải xuống WPP2019_TotalPopulationBySex.csv
từ trang web của Liên hợp quốc :
data/
│
├── __init__.py
└── WPP2019_TotalPopulationBySex.csv
Mở tệp CSV và xem tài liệu :
LocID,Location,VarID,Variant,Time,PopMale,PopFemale,PopTotal,PopDensity
4,Afghanistan,2,Medium,1950,4099.243,3652.874,7752.117,11.874
4,Afghanistan,2,Medium,1951,4134.756,3705.395,7840.151,12.009
4,Afghanistan,2,Medium,1952,4174.45,3761.546,7935.996,12.156
4,Afghanistan,2,Medium,1953,4218.336,3821.348,8039.684,12.315
...
Mỗi dòng chứa dân số của một vương quốc trong một năm nhất định và một biến thể nhất định, cho biết loại ngữ cảnh nào được sử dụng cho dự báo. Tệp này chứa những dự báo về dân số cho đến năm 2100 .
Hàm sau đọc tệp này và chọn ra tổng dân số của mỗi quốc gia cho một year
và variant
:
import csv
from importlib import resources
def read_population_file(year, variant=" Medium ") :
population = { }
print(f" Reading population data for{year},{variant}scenario ")
with resources.open_text(
" data ", " WPP2019_TotalPopulationBySex. csv "
) as fid:
rows = csv.DictReader(fid)
# Read data, filter the correct year
for row in rows:
if row[" Time "] = = year and row[" Variant "] = = variant:
pop = round(float(row[" PopTotal "] ) * 1000)
population[row[" Location "] ] = pop
return population
Các dòng được đánh dấu hiển thị cách importlib.resources
được sử dụng để mở tệp dữ liệu. Để biết thêm thông tin về cách làm việc với tệp CSV, hãy xem Đọc và Viết tệp CSV bằng Python .
Hàm trên trả về một từ điển với những số dân số :
>> >
>> >population = read_population_file(" 2020 ")
Reading population data for 2020, Medium scenario
>> >population[" Norway "]
5421242
Bạn hoàn toàn có thể làm bất kể điều gì mê hoặc với từ điển dân số này, gồm có cả nghiên cứu và phân tích và tưởng tượng. Tại đây, bạn sẽ tạo một game show đố nhu yếu người dùng xác lập vương quốc nào trong tập hợp là đông dân nhất. Chơi game show sẽ trông giống như sau :
USDpython population_quiz.py
Question 1 :
1. Tunisia
2. Djibouti
3. Belize
Which country has the largest population ? 1
Yes, Tunisia is most populous ( 11,818,618 )
Question 2 :
1. Mozambique
2. Ghana
3. Hungary
Which country has the largest population ? 2
No, Mozambique ( 31,255,435 ) is more populous than Ghana ( 31,072,945 )
...
Các cụ thể của việc triển khai nằm quá xa chủ đề của hướng dẫn này, vì thế chúng sẽ không được tranh luận ở đây. Tuy nhiên, bạn hoàn toàn có thể lan rộng ra phần bên dưới để xem mã nguồn hoàn hảo .
Nguồn Mã Dân số Trắc nghiệmHiện an
Ví dụ: Thêm biểu tượng vào Tkinter GUIs
Khi xây dựng giao diện người dùng đồ họa (GUI), bạn thường cần bao gồm các tệp tài nguyên như biểu tượng. Ví dụ sau đây cho thấy cách bạn có thể làm điều đó bằng cách sử dụng importlib.resources
. Ứng dụng cuối cùng sẽ trông khá cơ bản, nhưng nó sẽ có biểu tượng tùy chỉnh cũng như hình minh họa trên nút Tạm biệt :
Ví dụ sử dụng Tkinter, là một gói GUI có sẵn trong thư viện tiêu chuẩn. Nó dựa trên mạng lưới hệ thống hành lang cửa số Tk, bắt đầu được tăng trưởng cho ngôn từ lập trình Tcl. Có nhiều gói GUI khác có sẵn cho Python. Nếu bạn đang sử dụng một hình tượng khác, thì bạn sẽ hoàn toàn có thể thêm những hình tượng vào ứng dụng của mình bằng cách sử dụng những ý tưởng sáng tạo tương tự như như những hình tượng được trình diễn ở đây .
Trong Tkinter, hình ảnh được xử lý bởi PhotoImage
lớp . Để tạo một PhotoImage
, bạn chuyển một đường dẫn đến một tệp hình ảnh.
Hãy nhớ rằng, khi phân phối gói của bạn, bạn thậm chí không được đảm bảo rằng các tệp tài nguyên sẽ tồn tại dưới dạng tệp vật lý trên hệ thống tệp. importlib.resources
giải quyết điều này bằng cách cung cấp path()
. Hàm này sẽ trả về một đường dẫn đến tệp tài nguyên, tạo một tệp tạm thời nếu cần.
Để đảm bảo mọi tệp tạm thời được dọn dẹp đúng cách, bạn nên sử dụng path()
làm trình quản lý ngữ cảnh bằng từ khóa with
:
>> >
>> >from importlib import resources
>> >with resources.path(" hello_gui. gui_resources ", " logo.png ") as path:
... print(path)
...
/ home / gahjelle / hello_gui / gui_resources / logo.png
Đối với ví dụ rất đầy đủ, giả sử bạn có cấu trúc phân cấp tệp sau :
hello_gui/
│
├── gui_resources/
│ ├── __init__.py
│ ├── hand.png
│ └── logo.png
│
└── __main__.py
Nếu bạn muốn tự mình thử ví dụ, thì bạn hoàn toàn có thể tải xuống những tệp này cùng với phần còn lại của mã nguồn được sử dụng trong hướng dẫn này bằng cách nhấp vào link bên dưới :
Lấy mã nguồn : Nhấp vào đây để lấy mã nguồn mà bạn sẽ sử dụng để tìm hiểu và khám phá về mạng lưới hệ thống nhập Python trong hướng dẫn này .
Mã được lưu trữ trong một tệp có tên đặc biệt __main__.py
. Tên này chỉ ra rằng tệp là điểm vào của gói. Có một __main__.py
tệp cho phép gói của bạn được thực thi với python -m
:
USDpython -m hello_gui
Để biết thêm thông tin về cách gọi một gói với -m
, hãy xem Cách xuất bản Gói Python nguồn mở lên PyPI .
GUI được định nghĩa trong một lớp được gọi là Hello
. Lưu ý rằng bạn sử dụng importlib.resources
để lấy đường dẫn của các tệp hình ảnh:
1# hello_gui / __main__. py
2
3import tkinter as tk
4from tkinter import ttk
5
6try:
7 from importlib import resources
8except ImportError:
9 import importlib_resources as resources
10
11class Hello(tk.Tk) :
12 def __init__(self, *args, * *kwargs) :
13 super( ).__init__(*args, * *kwargs)
14 self.wm_title(" Hello ")
15
16 # Read image, store a reference to it, and set it as an icon
17 with resources.path(" hello_gui. gui_resources ", " logo.png ") as path:
18 self._icon = tk.PhotoImage(file=path)
19 self.iconphoto(True, self._icon)
20
21 # Read image, create a button, and store a reference to the image
22 with resources.path(" hello_gui. gui_resources ", " hand.png ") as path:
23 hand = tk.PhotoImage(file=path)
24 button = ttk.Button(
25 self,
26 image=hand,
27 text=" Goodbye ",
28 command=self.quit,
29 compound=tk.LEFT, # Add the image to the left of the text
30 )
31 button._image = hand
32 button.pack(side=tk.TOP, padx=10, pady=10)
33
34if __name__ = = " __main__ ":
35 hello = Hello( )
36 hello.mainloop( )
Nếu bạn muốn khám phá thêm về cách thiết kế xây dựng GUI với Tkinter, hãy xem Lập trình GUI Python với Tkinter. Tài liệu chính thức cũng có một list tài nguyên tuyệt vời để khởi đầu và hướng dẫn tại TkDocs là một tài nguyên tuyệt vời khác chỉ ra cách sử dụng Tk trong những ngôn từ khác .
Lưu ý: Một nguồn gốc của sự nhầm lẫn và thất vọng khi làm việc với hình ảnh trong Tkinter là bạn phải đảm bảo rằng hình ảnh không được thu gom rác . Do cách Python và Tk tương tác, trình thu gom rác bằng Python (ít nhất là trong CPython ) không đăng ký rằng hình ảnh được sử dụng bởi .iconphoto()
và Button
.
Để bảo vệ rằng những hình ảnh được lưu giữ xung quanh, bạn nên thêm một tham chiếu đến chúng theo cách thủ công bằng tay. Bạn hoàn toàn có thể xem ví dụ về điều này trong đoạn mã trên ở dòng 18 và 31 .
Nhập động
Một trong những đặc điểm nổi bật của Python là nó là một ngôn ngữ rất năng động. Mặc dù đôi khi một ý tưởng tồi, bạn có thể làm nhiều việc để một chương trình Python khi nó đang chạy, bao gồm thêm các thuộc tính thành một lớp, xác định lại phương pháp, hoặc thay đổi docstring của một module. Ví dụ: bạn có thể thay đổi print()
để nó không làm bất cứ điều gì:
>> >
>> >print(" Hello dynamic world ! ")
Hello dynamic world !
>> ># Redefine the built-in print ( )
>> >print = lambda *args, * *kwargs: None
>> >print(" Hush, everybody ! ")
>> ># Nothing is printed
Về mặt kỹ thuật, bạn không định nghĩa lại print()
. Thay vào đó, bạn đang xác định một print()
cái khác phủ bóng cái tích hợp sẵn. Để quay lại sử dụng bản gốc print()
, bạn có thể xóa bản gốc tùy chỉnh của mình bằng del print
. Nếu bạn có khuynh hướng như vậy, bạn có thể phủ bóng bất kỳ đối tượng Python nào được tích hợp vào trình thông dịch.
Lưu ý: Trong ví dụ trên, bạn xác định lại print()
bằng cách sử dụng một hàm lambda. Bạn cũng có thể sử dụng một định nghĩa hàm bình thường:
>> >
>> >def print(*args, * *kwargs) :
...pass
Để tìm hiểu và khám phá thêm về những hàm lambda, hãy xem Cách sử dụng những hàm Lambda trong Python .
Trong phần này, bạn sẽ học cách nhập động bằng Python. Với chúng, bạn sẽ không phải quyết định hành động nhập những gì cho đến khi chương trình của bạn đang chạy .
Sử dụng importlib
Cho đến nay, bạn đã sử dụng import
từ khóa của Python để nhập các mô-đun và gói một cách rõ ràng. Tuy nhiên, toàn bộ máy móc nhập khẩu có sẵn trong importlib
gói và điều này cho phép bạn thực hiện việc nhập khẩu của mình một cách linh hoạt hơn. Tập lệnh sau yêu cầu người dùng nhập tên mô-đun, nhập mô-đun đó và in chuỗi tài liệu của nó:
# docreader.py
import importlib
module_name = input(" Name of module ? ")
module = importlib.import_module(module_name)
print(module.__doc__)
import_module()
trả về một đối tượng mô-đun mà bạn có thể liên kết với bất kỳ biến nào. Sau đó, bạn có thể coi biến đó như một mô-đun được nhập thường xuyên. Bạn có thể sử dụng tập lệnh như sau:
USDpython docreader.py
Name of module ? math
This module is always available. It provides access to the
mathematical functions defined by the C standard .
USDpython docreader.py
Name of module ? csv
CSV parsing and writing .
This module provides classes that assist in the reading and writing
of Comma Separated Value ( CSV ) files, and implements the interface
described by PEP 305. Although many CSV files are simple to parse ,
the format is not formally defined by a stable specification and
is subtle enough that parsing lines of a CSV file with something
like line.split ( ", " ) is bound to fail. The module supports three
basic APIs : reading, writing, and registration of dialects .
[ ... ]
Trong mỗi trường hợp, mô-đun được nhập động bằng import_module()
.
Ví dụ: Phương pháp ban đầu với các gói không gian tên
Hãy nghĩ lại ví dụ về serializers trước đó. Với serializers
việc triển khai dưới dạng gói không gian tên, bạn có khả năng thêm bộ tuần tự tùy chỉnh. Trong ví dụ ban đầu từ một hướng dẫn trước, các bộ nối tiếp được tạo sẵn thông qua một nhà máy sản xuất bộ nối tiếp. Sử dụng importlib
, bạn có thể làm điều gì đó tương tự.
Thêm mã sau vào serializers
gói không gian tên cục bộ của bạn :
# local / serializers / factory.py
import importlib
def get_serializer(format) :
try:
module = importlib.import_module(f" serializers .{format}")
serializer = getattr(module, f"{format.title( )}Serializer ")
except (ImportError, AttributeError) :
raise ValueError(f" Unknown format{format! r }") from None
return serializer( )
def serialize(serializable, format) :
serializer = get_serializer(format)
serializable.serialize(serializer)
return str(serializer)
Nhà get_serializer()
máy có thể tạo bộ tuần tự động dựa trên format
tham số và serialize()
sau đó có thể áp dụng bộ tuần tự cho bất kỳ đối tượng nào triển khai một .serialize()
phương thức.
Nhà máy đưa ra 1 số ít giả định can đảm và mạnh mẽ về việc đặt tên cho cả mô-đun và lớp chứa những bộ tuần tự riêng không liên quan gì đến nhau. Trong phần tiếp theo, bạn sẽ khám phá về kiến trúc plugin được cho phép linh động hơn .
Bây giờ bạn hoàn toàn có thể tạo lại ví dụ trước đó như sau :
>> >
>> >from serializers import factory
>> >from tuy nhiên import Song
>> >tuy nhiên = Song(song_id=" 1 ", title=" The Same River ", artist=" Riverside ")
>> >factory.serialize(tuy nhiên, " json ")
' { " id " : " 1 ", " title " : " The Same River ", " artist " : " Riverside " } '
>> >factory.serialize(tuy nhiên, " yaml ")
" artist : Riverside, id : ' 1 ', title : The Same River \ n "
>> >factory.serialize(tuy nhiên, " toml ")
ValueError : Unknown format ' toml '
Trong trường hợp này, bạn không cần nhập từng bộ nối tiếp một cách rõ ràng nữa. Thay vào đó, bạn chỉ định tên của bộ nối tiếp bằng một chuỗi. Chuỗi thậm chí còn hoàn toàn có thể được chọn bởi người dùng của bạn trong thời hạn chạy .
Lưu ý: Trong một gói thông thường, bạn có thể đã triển khai get_serializer()
và serialize()
trong một __init__.py
tệp. Điều đó sẽ cho phép bạn chỉ cần nhập serializers
và sau đó gọi serializers.serialize()
.
Tuy nhiên, các gói không gian tên không được phép sử dụng __init__.py
, vì vậy bạn cần phải triển khai các chức năng này trong một mô-đun riêng biệt.
Ví dụ sau cuối cho thấy rằng bạn cũng nhận được một thông tin lỗi khá nếu bạn nỗ lực tuần tự hóa sang một định dạng chưa được tiến hành .
Ví dụ: Một gói các plugin
Hãy xem xét một ví dụ khác về việc sử dụng nhập động. Bạn hoàn toàn có thể sử dụng mô-đun sau để thiết lập kiến trúc plugin linh động trong mã của mình. Điều này tương tự như như ví dụ trước, trong đó bạn hoàn toàn có thể cắm bộ tuần tự cho những định dạng khác nhau bằng cách thêm những mô-đun mới .
Một ứng dụng sử dụng plugin hiệu suất cao là công cụ trực quan hóa tò mò Keo. Keo hoàn toàn có thể đọc nhiều định dạng tài liệu khác nhau ra khỏi hộp. Tuy nhiên, nếu định dạng tài liệu của bạn không được tương hỗ, thì bạn hoàn toàn có thể viết trình tải tài liệu tùy chỉnh của riêng mình .
Bạn làm điều này bằng cách thêm một công dụng mà bạn trang trí và đặt ở một vị trí đặc biệt quan trọng để Keo thuận tiện tìm thấy. Bạn không cần phải đổi khác bất kể phần nào của mã nguồn Keo. Xem tài liệu để biết toàn bộ những cụ thể .
Bạn hoàn toàn có thể thiết lập một kiến trúc plugin tựa như mà bạn hoàn toàn có thể sử dụng trong những dự án Bất Động Sản của riêng mình. Trong kiến trúc, có hai Lever :
- Gói plugin là một tập hợp các plugin có liên quan tương ứng với một gói Python.
- Plugin là một hành vi tùy chỉnh được tạo sẵn trong một mô-đun Python.
Các plugins
mô-đun đó cho thấy nhiều kiến trúc plugin có các chức năng sau:
# plugins.py
def register(func) :
" " " Decorator for registering a new plugin " " "
def names(package) :
" " " List all plugins in one package " " "
def get(package, plugin) :
" " " Get a given plugin " " "
def call(package, plugin, *args, * *kwargs) :
" " " Call the given plugin " " "
def _import(package, plugin) :
" " " Import the given plugin file from a package " " "
def _import_all(package) :
" " " Import all plugins in a package " " "
def names_factory(package) :
" " " Create a names ( ) function for one package " " "
def get_factory(package) :
" " " Create a get ( ) function for one package " " "
def call_factory(package) :
" " " Create a call ( ) function for one package " " "
Các công dụng gốc được sử dụng để thêm công dụng vào những gói plugin một cách thuận tiện. Bạn sẽ thấy 1 số ít ví dụ về cách chúng được sử dụng trong thời hạn ngắn .
Xem xét toàn bộ những chi tiết cụ thể của mã này nằm ngoài khoanh vùng phạm vi của hướng dẫn này. Nếu bạn chăm sóc, thì bạn hoàn toàn có thể xem cách tiến hành bằng cách lan rộng ra phần bên dưới .
Mã nguồn hoàn hảo của plugins. pyHiện an
Hãy xem một số ví dụ về cách sử dụng plugin. Ví dụ đầu tiên là một greeter
gói mà bạn có thể sử dụng để thêm nhiều lời chào khác nhau vào ứng dụng của mình. Một kiến trúc plugin đầy đủ chắc chắn là quá mức cần thiết đối với ví dụ này, nhưng nó cho thấy cách các plugin hoạt động.
Giả sử bạn có greeter
gói sau :
greeter/
│
├── __init__.py
├── hello.py
├── howdy.py
└── yo.py
Mỗi greeter
mô-đun xác định một hàm nhận một name
đối số. Lưu ý cách tất cả chúng được đăng ký làm plugin bằng trình @register
trang trí:
# greeter / hello.py
import plugins
@ plugins.register
def greet(name) :
print(f" Hello{name}, how are you today ? ")
# greeter / howdy.py
import plugins
@ plugins.register
def greet(name) :
print(f" Howdy good{name}, honored to meet you ! ")
# greeter / yo.py
import plugins
@ plugins.register
def greet(name) :
print(f" Yo{name}, good times ! ")
Để khám phá thêm về trình trang trí và cách chúng được sử dụng, hãy xem Primer trên Trình trang trí Python .
Lưu ý : Để đơn giản hóa việc mày mò và nhập những plugin, tên của mỗi plugin dựa trên tên của mô-đun chứa nó thay vì tên công dụng. Điều này hạn chế bạn chỉ có một plugin cho mỗi tệp .
Để hoàn tất thiết lập greeter
dưới dạng gói plugin, bạn có thể sử dụng các chức năng của nhà máy plugins
để thêm chức năng vào greeter
chính gói:
# greeter / __init__. py
import plugins
greetings = plugins.names_factory(__package__)
greet = plugins.call_factory(__package__)
Bây giờ bạn có thể sử dụng greetings()
và greet()
như sau:
>> >
>> >import greeter
>> >greeter.greetings( )
[ ' hello ', ' howdy ', ' yo ' ]
>> >greeter.greet(plugin=" howdy ", name=" Guido ")
Howdy good Guido, honored to meet you !
Lưu ý rằng greetings()
tự động phát hiện tất cả các plugin có sẵn trong gói.
Bạn cũng hoàn toàn có thể chọn một cách linh động hơn plugin nào để gọi. Trong ví dụ sau, bạn chọn một plugin một cách ngẫu nhiên. Tuy nhiên, bạn cũng hoàn toàn có thể chọn một plugin dựa trên tệp thông số kỹ thuật hoặc thông tin người dùng nhập :
>> >
>> >import greeter
>> >import random
>> >greeting = random.choice(greeter.greetings( ) )
>> >greeter.greet(greeting, name=" Frida ")
Hello Frida, how are you today ?
>> >greeting = random.choice(greeter.greetings( ) )
>> >greeter.greet(greeting, name=" Frida ")
Yo Frida, good times !
Để khám phá và gọi các plugin khác nhau, bạn cần nhập chúng. Hãy xem nhanh cách plugins
xử lý nhập khẩu. Công việc chính được thực hiện trong hai chức năng sau bên trong plugins.py
:
import importlib
import pathlib
from importlib import resources
def _import(package, plugin) :
" " " Import the given plugin file from a package " " "
importlib.import_module(f"{package}.{plugin}")
def _import_all(package) :
" " " Import all plugins in a package " " "
files = resources.contents(package)
plugins = [f[ :-3] for f in files if f.endswith(". py ") and f[0] ! = " _ "]
for plugin in plugins:
_import(package, plugin)
_import()
trông thẳng thắn một cách lừa dối. Nó sử dụng importlib
để nhập một mô-đun. Nhưng có một số điều cũng xảy ra trong nền:
- Hệ thống nhập của Python đảm bảo rằng mỗi plugin chỉ được nhập một lần.
@register
decorator được xác định bên trong mỗi mô-đun plugin đăng ký mỗi plugin đã nhập.- Trong quá trình triển khai đầy đủ, cũng sẽ có một số lỗi xử lý để đối phó với các plugin bị thiếu.
_import_all()
khám phá tất cả các plugin trong một gói. Đây là cách nó hoạt động:
contents()
từimportlib.resources
danh sách tất cả các tệp bên trong một gói.- Kết quả được lọc để tìm các plugin tiềm năng.
- Mỗi tệp Python không bắt đầu bằng dấu gạch dưới được nhập.
- Các plugin trong bất kỳ tệp nào được phát hiện và đăng ký.
Hãy kết thúc phần này với phiên bản cuối cùng của gói không gian tên serializers . Một vấn đề nổi bật là get_serializer()
nhà máy đã đưa ra những giả định mạnh mẽ về việc đặt tên cho các lớp serializer. Bạn có thể làm cho điều này linh hoạt hơn bằng cách sử dụng các plugin.
Đầu tiên, thêm một dòng đăng ký từng bộ tuần tự. Đây là một ví dụ về cách nó được thực hiện trong bộ yaml
tuần tự:
# local / serializers / yaml.py
import plugins
import yaml
from serializers.json import JsonSerializer
@ plugins.register
class YamlSerializer(JsonSerializer) :
def __str__(self) :
return yaml.dump(self._current_object)
Tiếp theo, cập nhật get_serializers()
để sử dụng plugins
:
# local / serializers / factory.py
import plugins
get_serializer = plugins.call_factory(__package__)
def serialize(serializable, format) :
serializer = get_serializer(format)
serializable.serialize(serializer)
return str(serializer)
Bạn thực hiện get_serializer()
bằng cách sử dụng call_factory()
vì điều đó sẽ tự động khởi tạo từng bộ nối tiếp. Với việc tái cấu trúc này, bộ tuần tự hoạt động giống như trước đó. Tuy nhiên, bạn có thể linh hoạt hơn trong việc đặt tên cho các lớp serializer của mình.
Để biết thêm thông tin về cách sử dụng plugin, hãy xem PyPlugs trên PyPI và Plugin : Thêm tính linh động cho bản trình diễn Ứng dụng của bạn từ PyCon 2019 .
Hệ thống nhập Python
Bạn đã thấy nhiều cách để tận dụng mạng lưới hệ thống nhập của Python. Trong phần này, bạn sẽ khám phá thêm một chút ít về những gì xảy ra đằng sau khi những mô-đun và gói được nhập .
Như với hầu hết những phần của Python, mạng lưới hệ thống nhập hoàn toàn có thể được tùy chỉnh. Bạn sẽ thấy 1 số ít cách mà bạn hoàn toàn có thể đổi khác mạng lưới hệ thống nhập, gồm có tự động hóa tải xuống những gói bị thiếu từ PyPI và nhập tệp tài liệu như thể chúng là mô-đun .
Nhập nội bộ
Chi tiết của mạng lưới hệ thống nhập Python được miêu tả trong tài liệu chính thức. Ở Lever cao, ba điều xảy ra khi bạn nhập một mô-đun ( hoặc gói ). Mô-đun là :
- Tìm kiếm
- Nạp vào
- Bị ràng buộc với một không gian tên
Đối với các lần nhập thông thường — những lần nhập được thực hiện với import
câu lệnh — cả ba bước đều diễn ra tự động. importlib
Tuy nhiên, khi bạn sử dụng , chỉ có hai bước đầu tiên là tự động. Bạn cần tự liên kết mô-đun với một biến hoặc không gian tên.
Ví dụ: các phương pháp nhập và đổi tên sau math.pi
đây gần như tương đương:
>> >
>> >from math import pi as PI
>> >PI
3.141592653589793
>> >import importlib
>> >_tmp = importlib.import_module(" math ")
>> >PI = _tmp.pi
>> >del _tmp
>> >PI
3.141592653589793
Tất nhiên, trong mã thông thường, bạn nên thích cái trước hơn .
Một điều cần chú ý quan tâm là, ngay cả khi bạn chỉ nhập một thuộc tính từ một mô-đun, hàng loạt mô-đun được tải và thực thi. Phần còn lại của nội dung của mô-đun chỉ không bị ràng buộc với khoảng trống tên hiện tại. Một cách để chứng minh điều này là xem xét những gì được gọi là bộ đệm ẩn mô-đun :
>> >
>> >from math import pi
>> >pi
3.141592653589793
>> >import sys
>> >sys.modules[" math "].cos(pi)
- 1.0
sys.modules
hoạt động như một bộ nhớ cache của mô-đun. Nó chứa các tham chiếu đến tất cả các mô-đun đã được nhập.
Bộ đệm ẩn mô-đun đóng một vai trò rất quan trọng trong hệ thống nhập Python. Nơi đầu tiên Python tìm kiếm các mô-đun khi thực hiện nhập là ở trong sys.modules
. Nếu một mô-đun đã có sẵn, thì mô-đun đó sẽ không được tải lại.
Đây là một sự tối ưu hóa tuyệt vời, nhưng nó cũng là một điều thiết yếu. Nếu những mô-đun được tải lại mỗi khi chúng được nhập, thì bạn hoàn toàn có thể gặp phải sự xích míc trong 1 số ít trường hợp nhất định, ví dụ điển hình như khi mã nguồn cơ bản biến hóa trong khi tập lệnh đang chạy .
Nhớ lại đường dẫn nhập bạn đã thấy trước đó. Về cơ bản, nó cho Python biết nơi tìm kiếm những mô-đun. Tuy nhiên, nếu Python tìm thấy một mô-đun trong bộ đệm ẩn của mô-đun, thì nó sẽ không bận tâm tìm kiếm đường dẫn nhập cho mô-đun .
Ví dụ: Singletons dưới dạng Mô-đun
Trong lập trình hướng đối tượng người tiêu dùng, một singleton là một lớp có nhiều nhất một bộc lộ. Mặc dù hoàn toàn có thể tiến hành những singleton bằng Python, nhưng thay vào đó, hầu hết những ứng dụng tốt của những singleton hoàn toàn có thể được giải quyết và xử lý bởi những mô-đun. Bạn hoàn toàn có thể tin yêu bộ đệm ẩn mô-đun để khởi tạo một lớp chỉ một lần .
Để làm ví dụ, hãy quay lại tài liệu dân số của Liên hợp quốc mà bạn đã thấy trước đó. Mô-đun sau định nghĩa một lớp phủ bọc tài liệu dân số :
# population.py
import csv
from importlib import resources
import matplotlib.pyplot as plt
class _Population:
def __init__(self) :
" " " Read the population file " " "
self.data = { }
self.variant = " Medium "
print(f" Reading population data for{self.variant}scenario ")
with resources.open_text
Xem thêm: Top 7 Phần Mềm Đổi Đuôi Video Nhẹ Và Nhanh Nhất 2021, Download Total Video Converter 3 – bacxiunong
(
" data ", " WPP2019_TotalPopulationBySex. csv "
) as fid:
rows = csv.DictReader(fid)
# Read data, filter the correct variant
for row in rows:
if int(row[" LocID "] ) > = 900 or row[" Variant "] ! = self.variant:
continue
country = self.data.setdefault(row[" Location "], { } )
population = float(row[" PopTotal "] ) * 1000
country[int(row[" Time "] ) ] = round(population)
def get_country(self, country) :
" " " Get population data for one country " " "
data = self.data[country]
years, population = zip(*data.items( ) )
return years, population
def plot_country(self, country) :
" " " Plot data for one country, population in millions " " "
years, population = self.get_country(country)
plt.plot(years, [p / 1 e6 for p in population], label=country)
def order_countries(self, year) :
" " " Sort countries by population in decreasing order " " "
countries = {c: self.data[c] [year] for c in self.data}
return sorted(countries, key=lambda c: countries[c], reverse=True)
# Instantiate the Singleton
data = _Population( )
Việc đọc tài liệu từ đĩa mất một chút ít thời hạn. Vì bạn không mong đợi tệp tài liệu đổi khác, bạn khởi tạo lớp khi bạn tải mô-đun. Tên của lớp khởi đầu bằng dấu gạch dưới để cho người dùng biết rằng họ không nên sử dụng nó .
Bạn có thể sử dụng population.data
singleton để tạo biểu đồ Matplotlib hiển thị dự báo dân số cho các quốc gia đông dân nhất:
>> >
>> >import matplotlib.pyplot as plt
>> >import population
Reading population data for Medium scenario
>> ># Pick out five most populous countries in 2050
>> >for country in population.data.order_countries(2050) [ :5] :
... population.data.plot_country(country)
...
>> >plt.legend( )
>> >plt.xlabel(" Year ")
>> >plt.ylabel(" Population [ Millions ] ")
>> >plt.title(" UN Population Projections ")
>> >plt.show( )
Điều này tạo ra một biểu đồ như sau :
Lưu ý rằng tải tài liệu vào thời gian nhập khẩu là một loại antipattern. Tốt nhất, bạn muốn hàng nhập khẩu của mình không có tính năng phụ càng tốt. Một cách tiếp cận tốt hơn là tải tài liệu một cách lười biếng khi bạn cần. Bạn hoàn toàn có thể làm điều này một cách khá lịch sự bằng cách sử dụng thuộc tính Mở rộng phần sau để xem ví dụ .
Đang tải tài liệu dân số một cách lười biếngHiện an
Đang tải lại các mô-đun
Bộ nhớ cache của mô-đun hoàn toàn có thể hơi không dễ chịu khi bạn đang thao tác trong trình thông dịch tương tác. Việc tải lại một mô-đun sau khi bạn biến hóa nó không phải là chuyện nhỏ. Ví dụ : hãy xem mô-đun sau :
# number.py
answer = 24
Là một phần của thử nghiệm và gỡ lỗi mô-đun này, bạn nhập nó vào bảng điều khiển và tinh chỉnh Python :
>> >
>> >import number
>> >number.answer
24
Giả sử bạn nhận ra rằng bạn có lỗi trong mã của mình, vì vậy bạn cập nhật number.py
tệp trong trình chỉnh sửa của mình:
# number.py
answer = 42
Quay lại bảng tinh chỉnh và điều khiển của bạn, bạn nhập mô-đun đã update để xem hiệu suất cao của việc sửa lỗi của bạn :
>> >
>> >import number
>> >number.answer
24
Tại sao câu trả lời vẫn là 24
? Bộ nhớ cache của mô-đun đang thực hiện điều kỳ diệu (bây giờ gây khó chịu) của nó: vì Python đã nhập number
trước đó, nó không có lý do gì để tải lại mô-đun mặc dù bạn vừa thay đổi nó.
Giải pháp đơn thuần nhất cho điều này là thoát khỏi bảng tinh chỉnh và điều khiển Python và khởi động lại nó. Điều này buộc Python cũng phải xóa bộ nhớ cache mô-đun của nó :
>> >
>> >import number
>> >number.answer
42
Tuy nhiên, việc khởi động lại trình thông dịch không phải lúc nào cũng khả thi. Bạn có thể đang ở trong một phiên phức tạp hơn khiến bạn mất nhiều thời gian để thiết lập. Nếu đúng như vậy, bạn có thể sử dụng importlib.reload()
để tải lại mô-đun thay thế:
>> >
>> >import number
>> >number.answer
24
>> ># Update number.py in your editor
>> >import importlib
>> >importlib.reload(number)
>> >number.answer
42
Lưu ý rằng reload()
yêu cầu một đối tượng mô-đun, không phải một chuỗi như import_module()
vậy. Ngoài ra, hãy lưu ý rằng reload()
có một số lưu ý. Đặc biệt, các biến tham chiếu đến các đối tượng trong một mô-đun không bị ràng buộc lại với các đối tượng mới khi mô-đun đó được tải lại. Xem tài liệu để biết thêm chi tiết.
Finders and Loaders
Bạn đã thấy trước đó rằng việc tạo các mô-đun có cùng tên với các thư viện tiêu chuẩn có thể tạo ra vấn đề. Ví dụ: nếu bạn có một tệp có tên math.py
trong đường dẫn nhập của Python, thì bạn sẽ không thể nhập math
từ thư viện chuẩn.
Tuy nhiên, điều này không phải luôn luôn như vậy. Tạo một tệp có tên time.py
với nội dung sau:
# time.py
print(" Now's the time ! ")
Tiếp theo, mở trình thông dịch Python và nhập mô-đun mới này :
>> >
>> >import time
>> ># Nothing is printed
>> >time.ctime( )
' Mon Jun 15 14:26:12 2020 '
>> >time.tzname
( ' CET ', ' CEST ' )
Có điều gì đó kỳ lạ đã xảy ra. Có vẻ như Python không phải là nhập time
mô-đun mới của bạn . Thay vào đó, nó đã nhập time
mô-đun từ thư viện chuẩn . Tại sao các mô-đun thư viện tiêu chuẩn hoạt động không nhất quán? Bạn có thể nhận được một gợi ý bằng cách kiểm tra các mô-đun:
>> >
>> >import math
>> >math
>> >import time
>> >time
Bạn có thể thấy nó math
được nhập từ một tệp, trong khi đó time
là một số loại mô-đun tích hợp sẵn. Có vẻ như các mô-đun tích hợp sẵn không bị che khuất bởi các mô-đun cục bộ.
Lưu ý: Các mô-đun tích hợp được biên dịch thành trình thông dịch Python. Thông thường, chúng module nền tảng như builtins
, sys
, và time
. Mô-đun nào được tích hợp tùy thuộc vào trình thông dịch Python của bạn, nhưng bạn có thể tìm thấy tên của chúng trong đó sys.builtin_module_names
.
Hãy tìm hiểu và khám phá sâu hơn nữa về mạng lưới hệ thống nhập của Python. Điều này cũng sẽ cho thấy nguyên do tại sao những mô-đun tích hợp sẵn không bị che bởi những mô-đun cục bộ. Có một số ít bước tương quan khi nhập một mô-đun :
-
Python kiểm tra xem mô-đun có sẵn trong bộ nhớ cache của mô-đun hay không . Nếu
sys.modules
chứa tên của mô-đun, thì mô-đun đó đã có sẵn và quá trình nhập kết thúc. - Python khởi đầu tìm kiếm mô-đun bằng cách sử dụng một số ít công cụ tìm kiếm. Một công cụ tìm kiếm sẽ tìm kiếm mô-đun bằng cách sử dụng một kế hoạch nhất định. Công cụ tìm kiếm mặc định hoàn toàn có thể nhập mô-đun tích hợp sẵn, mô-đun cố định và thắt chặt và mô-đun trên đường dẫn nhập .
- Python tải mô-đun bằng trình tải. Trình tải mà Python sử dụng được xác lập bởi công cụ tìm kiếm xác định mô-đun và được chỉ định trong một thứ gọi là thông số kỹ thuật mô-đun .
Bạn hoàn toàn có thể lan rộng ra mạng lưới hệ thống nhập Python bằng cách tiến hành công cụ tìm của riêng bạn và nếu cần, trình tải của riêng bạn. Bạn sẽ thấy một ví dụ có ích hơn về công cụ tìm kiếm sau này. Hiện tại, bạn sẽ học cách triển khai những tùy chỉnh cơ bản ( và hoàn toàn có thể ngớ ngẩn ) của mạng lưới hệ thống nhập .
sys.meta_path
kiểm soát công cụ tìm kiếm nào được gọi trong quá trình nhập:
>> >
>> >import sys
>> >sys.meta_path
[,
,
]
Trước tiên, hãy lưu ý rằng điều này trả lời câu hỏi trước đó: các mô-đun tích hợp sẵn không bị che khuất bởi các mô-đun cục bộ vì công cụ tìm tích hợp được gọi trước công cụ tìm đường dẫn nhập, công cụ này tìm thấy các mô-đun cục bộ. Thứ hai, lưu ý rằng bạn có thể tùy chỉnh sys.meta_path
theo ý thích của mình.
Để nhanh gọn làm rối phiên Python của bạn, bạn hoàn toàn có thể xóa tổng thể những công cụ tìm kiếm :
>> >
>> >import sys
>> >sys.meta_path.clear( )
>> >sys.meta_path
[ ]
>> >import math
Traceback ( most recent call last ) :
File ""
, line 1, in
ModuleNotFoundError: No module named ' math '
>> >import importlib # Autoimported at start-up, still in the module cache
>> >importlib
Vì không có công cụ tìm kiếm, Python không hề tìm hoặc nhập những mô-đun mới. Tuy nhiên, Python vẫn hoàn toàn có thể nhập những mô-đun đã có trong bộ nhớ cache của mô-đun vì nó trông ở đó trước khi gọi bất kỳ công cụ tìm kiếm nào .
Trong ví dụ trên, importlib
đã được tải dưới mui xe trước khi bạn xóa danh sách công cụ tìm kiếm. Nếu bạn thực sự muốn làm cho phiên Python của mình hoàn toàn không sử dụng được, thì bạn cũng có thể xóa bộ nhớ cache của mô-đun , sys.modules
.
Sau đây là một ví dụ có ích hơn một chút ít. Bạn sẽ viết một công cụ tìm để in một thông tin tới bảng tinh chỉnh và điều khiển xác lập mô-đun đang được nhập. Ví dụ cho thấy cách thêm công cụ tìm của riêng bạn, mặc dầu nó không thực sự nỗ lực tìm một mô-đun :
1# debug_importer. py
2
3import sys
4
5class DebugFinder:
6 @ classmethod
7 def find_spec(cls, name, path, target=None) :
8 print(f" Importing{name! r }")
9 return None
10
11sys.meta_path.insert(0, DebugFinder)
Tất cả các trình tìm kiếm phải triển khai một .find_spec()
phương thức lớp, phương thức này sẽ cố gắng tìm một mô-đun nhất định. Có ba cách .find_spec()
có thể chấm dứt:
- Bằng cách quay lại
None
nếu nó không biết cách tìm và tải mô-đun - Bằng cách trả về một mô-đun chỉ định cách tải mô-đun
- Bằng cách tăng a
ModuleNotFoundError
để cho biết rằng không thể nhập mô-đun
Bản DebugFinder
in một thông báo đến bảng điều khiển và sau đó trả về một cách rõ ràng None
để chỉ ra rằng các công cụ tìm kiếm khác sẽ tìm ra cách thực sự nhập mô-đun.
Lưu ý: Vì Python trả về ngầm địnhNone
từ bất kỳ hàm hoặc phương thức nào mà không có hàm rõ ràng return
, bạn có thể bỏ qua dòng 9 . Tuy nhiên, trong trường hợp này, tốt hơn là nên bao gồm return None
để làm rõ rằng DebugFinder
không tìm thấy mô-đun.
Bằng cách chèn DebugFinder
đầu tiên vào danh sách công cụ tìm kiếm, bạn sẽ nhận được danh sách đang chạy của tất cả các mô-đun đang được nhập:
>> >
>> >import debug_importer
>> >import csv
Importing ' csv '
Importing ' re '
Importing ' enum '
Importing ' sre_compile '
Importing ' _sre '
Importing ' sre_parse '
Importing ' sre_constants '
Importing ' copyreg '
Importing ' _csv '
Ví dụ: bạn có thể thấy rằng việc nhập csv
sẽ kích hoạt việc nhập một số mô-đun khác csv
phụ thuộc vào. Lưu ý rằng tùy chọn dài dòng cho trình thông dịch Python python -v
, cung cấp cùng một thông tin và nhiều hơn nữa.
Ví dụ khác, giả sử rằng bạn đang tìm cách loại bỏ thế giới của các biểu thức chính quy . (Bây giờ, tại sao bạn lại muốn một thứ như vậy? Biểu thức chính quy thật tuyệt vời !) Bạn có thể triển khai công cụ tìm kiếm sau để cấm re
mô-đun biểu thức chính quy :
# ban_importer. py
import sys
BANNED_MODULES = {" re "}
class BanFinder:
@ classmethod
def find_spec(cls, name, path, target=None) :
if name in BANNED_MODULES:
raise ModuleNotFoundError(f"{name! r }is banned ")
sys.meta_path.insert(0, BanFinder)
Nâng cao một ModuleNotFoundError
đảm bảo rằng không có công cụ tìm kiếm nào sau này trong danh sách các công cụ tìm kiếm sẽ được thực thi. Điều này có hiệu quả ngăn bạn sử dụng các biểu thức chính quy trong Python:
>> >
>> >import ban_importer
>> >import csv
Traceback ( most recent call last ) :
File ""
, line 1, in
File " ... / python / lib / python3. 8 / csv.py ", line 6, in
import re
File " ban_importer. py ", line 11, in find_spec
raise ModuleNotFoundError(f"{name! r }is banned ")
ModuleNotFoundError: ' re ' is banned
Mặc dù bạn chỉ đang nhập csv
, nhưng mô-đun đó đang nhập re
ở hậu trường, do đó, lỗi sẽ xuất hiện.
Ví dụ: Tự động cài đặt từ PyPI
Bởi vì mạng lưới hệ thống nhập Python đã khá can đảm và mạnh mẽ và có ích, có nhiều cách để làm rối tung nó hơn là lan rộng ra nó một cách hữu dụng. Tuy nhiên, ví dụ sau đây hoàn toàn có thể hữu dụng trong một số ít trường hợp nhất định .
Các Python Package Index (PyPI) là một cửa của bạn cửa hàng phô mai cho việc tìm kiếm các module và các gói của bên thứ ba. Nó cũng là nơi pip
tải xuống các gói.
Trong các hướng dẫn Real Python khác , bạn có thể đã xem hướng dẫn sử dụng python -m pip install
để cài đặt các gói và mô-đun của bên thứ ba mà bạn cần để làm theo cùng với các ví dụ. Sẽ không tuyệt vời nếu Python tự động cài đặt các mô-đun bị thiếu cho bạn phải không?
Cảnh báo: Trong hầu hết các trường hợp, thực sự sẽ không tuyệt vời nếu Python tự động cài đặt các mô-đun. Ví dụ: trong hầu hết các cài đặt sản xuất, bạn muốn kiểm soát môi trường của mình. Hơn nữa, tài liệu cảnh báo không sử dụng pip
theo cách này.
Để tránh làm trộn lẫn quy trình setup Python của bạn, bạn chỉ nên sử dụng mã này trong những môi trường tự nhiên mà bạn không ngại xóa hoặc thiết lập lại .
Công cụ tìm kiếm sau cố gắng cài đặt các mô-đun bằng cách sử dụng pip
:
# pip_importer. py
from importlib import util
import subprocess
import sys
class PipFinder:
@ classmethod
def find_spec(cls, name, path, target=None) :
print(f" Module{name! r }not installed. Attempting to pip install ")
cmd = f"{sys.executable}- m pip install{name}"
try:
subprocess.run(cmd.split( ), check=True)
except subprocess.CalledProcessError:
return None
return util.find_spec(name)
sys.meta_path.append(PipFinder)
So với các công cụ tìm bạn đã thấy trước đó, công cụ này phức tạp hơn một chút. Bằng cách đặt công cụ tìm kiếm này cuối cùng trong danh sách công cụ tìm kiếm, bạn biết rằng nếu bạn gọi PipFinder
, thì mô-đun sẽ không được tìm thấy trên hệ thống của bạn. Công việc của .find_spec()
do đó chỉ là làm pip install
. Nếu quá trình cài đặt hoạt động, thì thông số mô-đun sẽ được tạo và trả về.
Cố gắng sử dụng parse
thư viện mà không cần tự cài đặt nó:
>> >
>> >import pip_importer
>> >import parse
Module ' parse ' not installed. Attempting to pip install
Collecting parse
Downloading parse-1.15.0.tar.gz ( 29 kB )
Building wheels for collected packages : parse
Building wheel for parse ( setup.py ) ... done
Successfully built parse
Installing collected packages : parse
Successfully installed parse-1. 15.0
>> >pattern = " my name is{ name }"
>> >parse.parse(pattern, " My name is Geir Arne ")
Thông thường, import parse
sẽ tăng a ModuleNotFoundError
, nhưng trong trường hợp parse
này là cài đặt và nhập.
Mặc dù PipFinder
có vẻ hiệu quả, nhưng có một số thách thức với cách tiếp cận này. Một vấn đề chính là tên nhập của một mô-đun không phải lúc nào cũng tương ứng với tên của nó trên PyPI. Ví dụ: trình đọc nguồn cấp dữ liệu Python thực được gọi realpython-reader
trên PyPI, nhưng tên nhập chỉ đơn giản là reader
.
Sử dụng PipFinder
để nhập và cài đặt reader
kết thúc bằng cách cài đặt gói sai:
>> >
>> >import pip_importer
>> >import reader
Module ' reader ' not installed. Attempting to pip install
Collecting reader
Downloading reader-1.2-py3-none-any.whl ( 68 kB )
...
Điều này hoàn toàn có thể gây ra hậu quả tai hại cho dự án Bất Động Sản của bạn .
Một trường hợp trong đó thiết lập tự động hóa hoàn toàn có thể khá hữu dụng là khi bạn đang chạy Python trên đám mây với quyền trấn áp hạn chế hơn so với thiên nhiên và môi trường của bạn, ví dụ điển hình như khi bạn đang chạy sổ ghi chép kiểu Jupyter tại Google Colaboratory. Môi trường máy tính xách tay Colab rất tuyệt vời để thực thi tò mò tài liệu hợp tác .
Một máy tính xách tay điển hình đi kèm với nhiều gói khoa học dữ liệu được cài đặt, bao gồm NumPy , Pandas và Matplotlib và bạn có thể thêm các gói mới với pip
. Nhưng bạn cũng có thể kích hoạt cài đặt tự động:
Vì pip_importer
không khả dụng cục bộ trên máy chủ Colab, mã được sao chép vào ô đầu tiên của sổ ghi chép.
Ví dụ: Nhập tệp dữ liệu
Ví dụ cuối cùng trong phần này được lấy cảm hứng từ bài đăng blog tuyệt vời của Aleksey Bilogur Nhập hầu hết mọi thứ bằng Python: Giới thiệu về Trình tải và Trình tìm mô-đun . Bạn đã thấy cách sử dụng importlib.resources
để nhập tệp dữ liệu. Tại đây, thay vào đó, bạn sẽ triển khai trình tải tùy chỉnh có thể nhập trực tiếp tệp CSV.
Trước đó , bạn đã làm việc với một tệp CSV khổng lồ với dữ liệu dân số. Để làm cho ví dụ về trình tải tùy chỉnh dễ quản lý hơn, hãy xem xét employees.csv
tệp nhỏ hơn sau :
name,department,birthday month
John Smith,Accounting,November
Erica Meyers,IT,March
Dòng tiên phong là tiêu đề đặt tên cho ba trường và hai hàng tài liệu sau, mỗi hàng chứa thông tin về một nhân viên cấp dưới. Để biết thêm thông tin về cách thao tác với tệp CSV, hãy xem Đọc và Viết tệp CSV bằng Python .
Mục tiêu của bạn trong phần này là viết một công cụ tìm và một trình tải được cho phép bạn nhập trực tiếp tệp CSV để bạn hoàn toàn có thể viết mã như sau :
>> >
>> >import csv_importer
>> >import employees
>> >employees.name
( ' John Smith ', ' Erica Meyers ' )
>> >for row in employees.data:
... print(row[" department "] )
...
Accounting
IT
>> >for name, month in zip(employees.name, employees.birthday_month) :
... print(f"{name}is born in{month}")
...
John Smith is born in November
Erica Meyers is born in March
>> >employees.__file__
' employees.csv '
Công việc của công cụ tìm kiếm sẽ là tìm kiếm và nhận dạng những tệp CSV. Công việc của trình tải sẽ là nhập tài liệu CSV. Thông thường, bạn hoàn toàn có thể tiến hành những trình tìm kiếm và trình tải tương ứng trong một lớp chung. Đó là cách tiếp cận bạn sẽ thực thi ở đây :
1# csv_importer. py
2
3import csv
4import pathlib
5import re
6import sys
7from importlib.machinery import ModuleSpec
8
9class CsvImporter( ) :
10 def __init__(self, csv_path) :
11 " " " Store path to CSV file " " "
12 self.csv_path = csv_path
13
14 @ classmethod
15 def find_spec(cls, name, path, target=None) :
16 " " " Look for CSV file " " "
17 package, _, module_name = name.rpartition(". ")
18 csv_file_name = f"{module_name}. csv "
19 directories = sys.path if path is None else path
20 for directory in directories:
21 csv_path = pathlib.Path(directory) / csv_file_name
22 if csv_path.exists( ) :
23 return ModuleSpec(name, cls(csv_path) )
24
25 def create_module(self, spec) :
26 " " " Returning None uses the standard machinery for creating modules " " "
27 return None
28
29 def exec_module(self, module) :
30 " " " Executing the module means reading the CSV file " " "
31 # Read CSV data and store as a list of rows
32 with self.csv_path.open( ) as fid:
33 rows = csv.DictReader(fid)
34 data = list(rows)
35 fieldnames = tuple(_identifier(f) for f in rows.fieldnames)
36
37 # Create a dict with each field
38 values = zip(*(row.values( ) for row in data) )
39 fields = dict(zip(fieldnames, values) )
40
41 # Add the data to the module
42 module.__dict__.update(fields)
43 module.__dict__[" data "] = data
44 module.__dict__[" fieldnames "] = fieldnames
45 module.__file__ = str(self.csv_path)
46
47 def __repr__(self) :
48 " " " Nice representation of the class " " "
49 return f"{self.__class__.__name__}({str(self.csv_path)! r }) "
50
51def _identifier(var_str) :
52 " " " Create a valid identifier from a string
53
54See https://stackoverflow.com/a/3305731
55" " "
56 return re.sub(r" \ W | ^ ( ? = \ d ) ", " _ ", var_str)
57
58# Add the CSV importer at the end of the list of finders
59sys.meta_path.append(CsvImporter)
Có khá nhiều mã trong ví dụ này! May mắn thay, hầu hết công việc được thực hiện trong .find_spec()
và .exec_module()
. Hãy xem xét chúng chi tiết hơn.
Như bạn đã thấy trước đó, .find_spec()
chịu trách nhiệm tìm kiếm mô-đun. Trong trường hợp này, bạn đang tìm kiếm tệp CSV, vì vậy bạn tạo tên tệp có .csv
hậu tố. name
chứa tên đầy đủ của mô-đun được nhập. Ví dụ, nếu bạn sử dụng from data import employees
, thì name
sẽ được data.employees
. Trong trường hợp này, tên tệp sẽ là employees.csv
.
Đối với nhập khẩu cấp cao nhất, path
sẽ được None
. Trong trường hợp đó, bạn tìm kiếm tệp CSV trong đường dẫn nhập đầy đủ, đường dẫn này sẽ bao gồm thư mục làm việc hiện tại. Nếu bạn đang nhập tệp CSV trong một gói, thì tệp đó path
sẽ được đặt thành đường dẫn hoặc các đường dẫn của gói. Nếu bạn tìm thấy tệp CSV phù hợp, thì thông số mô-đun sẽ được trả về. Thông số mô-đun này yêu cầu Python tải mô-đun bằng cách sử dụng CsvImporter
.
Dữ liệu CSV được tải bởi .exec_module()
. Bạn có thể sử dụng csv.DictReader
từ thư viện chuẩn để phân tích cú pháp tệp thực tế. Giống như hầu hết mọi thứ trong Python, các mô-đun được hỗ trợ bởi từ điển. Bằng cách thêm dữ liệu CSV vào module.__dict__
, bạn làm cho nó có sẵn dưới dạng thuộc tính của mô-đun.
Ví dụ: thêm fieldnames
vào từ điển mô-đun trên dòng 44 cho phép bạn liệt kê tên trường trong tệp CSV như sau:
>> >
>> >employees.fieldnames
( ' name ', ' department ', ' birthday_month ' )
Nói chung, tên trường CSV có thể chứa khoảng trắng và các ký tự khác không được phép trong tên thuộc tính Python. Trước khi thêm các trường làm thuộc tính trên mô-đun, bạn làm sạch tên trường bằng biểu thức chính quy . Điều này được thực hiện khi _identifier()
bắt đầu từ dòng 51 .
Bạn có thể xem ví dụ về hiệu ứng này trong birthday_month
tên trường ở trên. Nếu bạn nhìn vào tệp CSV ban đầu, thì bạn sẽ thấy rằng tiêu đề birthday month
có dấu cách thay vì dấu gạch dưới.
Bằng cách kết nối điều này CsvImporter
vào hệ thống nhập Python, bạn sẽ có được một chút chức năng miễn phí. Ví dụ: bộ đệm ẩn mô-đun sẽ đảm bảo rằng tệp dữ liệu chỉ được tải một lần.
Mẹo và thủ thuật nhập khẩu
Để triển khai xong hướng dẫn này, bạn sẽ thấy một vài mẹo về cách giải quyết và xử lý những trường hợp nhất định nhiều lúc Open. Bạn sẽ thấy cách giải quyết và xử lý những gói bị thiếu, nhập theo chu kỳ luân hồi và thậm chí còn những gói được tàng trữ bên trong tệp ZIP .
Xử lý các gói trên các phiên bản Python
Đôi khi bạn cần xử lý các gói có tên khác nhau tùy thuộc vào phiên bản Python. Bạn đã thấy một ví dụ về điều này: importlib.resources
chỉ có sẵn kể từ Python 3.7. Trong các phiên bản Python trước đó, bạn cần cài đặt và sử dụng importlib_resources
thay thế.
Miễn là các phiên bản khác nhau của gói tương thích, bạn có thể xử lý điều này bằng cách đổi tên gói bằng as
:
try:
from importlib import resources
except ImportError:
import importlib_resources as resources
Trong phần còn lại của mã, bạn có thể tham khảo resources
và không phải lo lắng về việc bạn đang sử dụng importlib.resources
hoặc importlib_resources
.
Thông thường, dễ nhất là sử dụng một try...except
câu lệnh để tìm ra phiên bản nào sẽ sử dụng. Một tùy chọn khác là kiểm tra phiên bản của trình thông dịch Python. Tuy nhiên, điều này có thể thêm một số chi phí bảo trì nếu bạn cần cập nhật số phiên bản.
Bạn hoàn toàn có thể viết lại ví dụ trước như sau :
import sys
if sys.version_info > = (3, 7) :
from importlib import resources
else:
import importlib_resources as resources
Điều này sẽ sử dụng importlib.resources
trên Python 3.7 và mới hơn trong khi quay trở lại importlib_resources
các phiên bản cũ hơn của Python. Xem flake8-2020
dự án để có lời khuyên tốt và có thể kiểm chứng trong tương lai về cách kiểm tra phiên bản Python nào đang chạy.
Xử lý các gói bị thiếu: Sử dụng một gói thay thế
Trường hợp sử dụng sau có tương quan ngặt nghèo với ví dụ trước. Giả sử có một gói tái thực thi thích hợp. Việc triển khai lại được tối ưu hóa tốt hơn, thế cho nên bạn muốn sử dụng nó nếu nó có sẵn. Tuy nhiên, gói gốc dễ có sẵn hơn và cũng mang lại hiệu suất đồng ý được .
Một ví dụ như vậy là quicktions
, đó là phiên bản được tối ưu hóa fractions
từ thư viện chuẩn. Bạn có thể xử lý các tùy chọn này giống như cách bạn xử lý các tên gói khác nhau trước đó:
try:
from quicktions import Fraction
except ImportError:
from fractions import Fraction
Điều này sẽ sử dụng quicktions
nếu nó có sẵn và rơi trở lại fractions
nếu không.
Một ví dụ tương tự khác là gói UltraJSON , một bộ mã hóa và giải mã JSON cực nhanh có thể được sử dụng để thay thế json
trong thư viện tiêu chuẩn:
try:
import ujson as json
except ImportError:
import json
Bằng cách đổi tên ujson
thành json
, bạn không phải lo lắng về gói nào đã thực sự được nhập.
Xử lý các gói bị thiếu: Sử dụng Mock để thay thế
Ví dụ thứ ba, có liên quan là thêm một gói cung cấp một tính năng tuyệt vời không cần thiết cho ứng dụng của bạn. Một lần nữa, điều này có thể được giải quyết bằng cách thêm try...except
vào các mục nhập của bạn. Thách thức bổ sung là bạn sẽ thay thế gói tùy chọn như thế nào nếu nó không có sẵn.
Ví dụ đơn cử, giả sử rằng bạn đang sử dụng Colorama để thêm văn bản màu trong bảng tinh chỉnh và điều khiển. Colorama đa phần gồm có những hằng số chuỗi đặc biệt quan trọng thêm màu khi in :
>> >
>> >import colorama
>> >colorama.init(autoreset=True)
>> >from colorama import Back, Fore
>> >Fore.RED
' \ x1b [ 31 m '
>> >print(f"{Fore.RED}Hello Color ! ")
Hello Color !
>> >print(f"{Back.RED}Hello Color ! ")
Hello Color !
Thật không may, màu không hiển thị trong ví dụ trên. Trong thiết bị đầu cuối của bạn, nó sẽ trông giống như sau :
Trước khi bắt đầu sử dụng màu Colorama, bạn nên gọi điện colorama.init()
. Đặt autoreset
thành True
có nghĩa là các chỉ thị màu sẽ được tự động đặt lại ở cuối chuỗi. Đó là một cài đặt hữu ích nếu bạn chỉ muốn tô màu một dòng tại một thời điểm.
Nếu bạn muốn tất cả đầu ra của mình là (ví dụ) màu xanh lam, thì bạn có thể để autoreset
là False
và thêm Fore.BLUE
vào đầu tập lệnh của mình. Các màu sau có sẵn:
>> >
>> >from colorama import Fore
>> >sorted(c for c in dir(Fore) if not c.startswith(" _ ") )
[ ' BLACK ', ' BLUE ', ' CYAN ', ' GREEN ', ' LIGHTBLACK_EX ', ' LIGHTBLUE_EX ' ,
' LIGHTCYAN_EX ', ' LIGHTGREEN_EX ', ' LIGHTMAGENTA_EX ', ' LIGHTRED_EX ' ,
' LIGHTWHITE_EX ', ' LIGHTYELLOW_EX ', ' MAGENTA ', ' RED ', ' RESET ' ,
' WHITE ', ' YELLOW ' ]
Bạn cũng có thể sử dụng colorama.Style
để kiểm soát kiểu văn bản của mình. Bạn có thể lựa chọn giữa DIM
, NORMAL
và BRIGHT
.
Cuối cùng, colorama.Cursor
cung cấp mã để kiểm soát vị trí của con trỏ. Bạn có thể sử dụng nó để hiển thị tiến trình hoặc trạng thái của một tập lệnh đang chạy. Ví dụ sau hiển thị đếm ngược từ 10
:
# countdown.py
import colorama
from colorama import Cursor, Fore
import time
colorama.init(autoreset=True)
countdown = [f"{Fore.BLUE} {n}" for n in range(10, 0, -1) ]
countdown.append(f"{Fore.RED}Lift off ! ")
print(f"{Fore.GREEN}Countdown starting :\ n")
for count in countdown:
time.sleep(1)
print(f"{Cursor.UP(1)} {count}")
Lưu ý cách bộ đếm giữ nguyên vị trí thay vì in trên những dòng riêng không liên quan gì đến nhau như thông thường :
Hãy quay trở lại trách nhiệm trong tầm tay. Đối với nhiều ứng dụng, việc thêm màu vào đầu ra bảng điều khiển và tinh chỉnh của bạn là một điều tuyệt vời nhưng không quá quan trọng. Để tránh thêm phụ thuộc vào khác vào ứng dụng của mình, bạn chỉ muốn sử dụng Colorama nếu nó có sẵn trên mạng lưới hệ thống và không phá vỡ ứng dụng nếu không có .
Để làm điều này, bạn hoàn toàn có thể lấy cảm hứng từ thử nghiệm và việc sử dụng mô phỏng. Một quy mô hoàn toàn có thể thay thế sửa chữa cho một đối tượng người tiêu dùng khác trong khi vẫn được cho phép bạn trấn áp hành vi của nó. Đây là một nỗ lực ngây thơ nhằm mục đích chế nhạo Colorama :
>> >
>> >from unittest.mock import Mock
>> >colorama = Mock( )
>> >colorama.init(autoreset=True)
>> >Fore = Mock( )
>> >Fore.RED
>> >print(f"{Fore.RED}Hello Color ! ")
Hello Color!
Điều này không hoàn toàn hiệu quả, vì Fore.RED
được biểu diễn bằng một chuỗi làm rối đầu ra của bạn. Thay vào đó, bạn muốn tạo một đối tượng luôn hiển thị dưới dạng chuỗi trống.
Có thể thay đổi giá trị trả lại .__str__()
trên Mock
các đối tượng. Tuy nhiên, trong trường hợp này, sẽ thuận tiện hơn khi viết mô hình của riêng bạn:
# optional_color. py
try:
from colorama import init, Back, Cursor, Fore, Style
except ImportError:
from collections import UserString
class ColoramaMock(UserString) :
def __call__(self, *args, * *kwargs) :
return self
def __getattr__(self, key) :
return self
init = ColoramaMock(" ")
Back = Cursor = Fore = Style = ColoramaMock(" ")
ColoramaMock("")
là một chuỗi trống cũng sẽ trả về chuỗi trống khi nó được gọi. Điều này giúp chúng ta tái hiện lại Colorama một cách hiệu quả, chỉ cần không có màu sắc.
Bí quyết cuối cùng là .__getattr__()
lợi nhuận riêng của mình, do đó tất cả các phong trào màu sắc, phong cách, và con trỏ đó là các thuộc tính trên Back
, Fore
, Style
, và Cursor
đang chế giễu là tốt.
Các optional_color
mô-đun được thiết kế để trở thành một thả thay thế cho Colorama, vì vậy bạn có thể cập nhật ví dụ đếm ngược sử dụng tìm kiếm và thay thế:
# countdown.py
import optional_color
from optional_color import Cursor, Fore
import time
optional_color.init(autoreset=True)
countdown = [f"{Fore.BLUE} {n}" for n in range(10, 0, -1) ]
countdown.append(f"{Fore.RED}Lift off ! ")
print(f"{Fore.GREEN}Countdown starting :\ n")
for count in countdown:
time.sleep(1)
print(f"{Cursor.UP(1)} {count}")
Nếu bạn chạy tập lệnh này trên một mạng lưới hệ thống không có sẵn Colorama, thì nó vẫn hoạt động giải trí, nhưng hoàn toàn có thể trông không đẹp :
Với Colorama được thiết lập, bạn sẽ thấy hiệu quả tương tự như như trước đó .
Nhập tập lệnh dưới dạng mô-đun
Một điểm độc lạ giữa những tập lệnh và mô-đun thư viện là những tập lệnh thường làm một việc gì đó, trong khi những thư viện phân phối công dụng. Cả hai tập lệnh và thư viện đều nằm bên trong những tệp Python thường thì và theo như Python có tương quan, không có sự độc lạ giữa chúng .
Thay vào đó, sự khác biệt nằm ở cách tệp được sử dụng: nó nên được thực thi với python file.py
hay được nhập với import file
bên trong một tập lệnh khác?
Đôi khi bạn sẽ có một mô-đun hoạt động giải trí như một tập lệnh và một thư viện. Bạn hoàn toàn có thể cố gắng nỗ lực cấu trúc lại mô-đun của mình thành hai tệp khác nhau .
Một ví dụ về điều này trong thư viện chuẩn là json
gói . Bạn thường sử dụng nó như một thư viện, nhưng nó cũng đi kèm với một tập lệnh có thể kiểm tra các tệp JSON. Giả sử bạn có colors.json
tệp sau :
{" colors ": [ {" color ": " blue ", " category ": " hue ", " type ": " primary ",
" code ": {" rgba ": [0,0,255,1], " hex ": " # 00F "} }, {" color ": " yellow ",
" category ": " hue ", " type ": " primary ", " code ": {" rgba ": [255,255,0,1] ,
" hex ": " # FF0 "} } ] }
Vì JSON thường chỉ được đọc bởi máy, nhiều tệp JSON không được định dạng theo cách hoàn toàn có thể đọc được. Trên thực tiễn, việc những tệp JSON gồm có một dòng văn bản rất dài là điều khá thông dụng .
json.tool
là một tập lệnh sử dụng json
thư viện để định dạng JSON theo cách dễ đọc hơn:
USDpython -m json.tool colors.json --sort-keys
{
" colors " : [
{
" category " : " hue " ,
" code " : {
" hex " : " # 00F " ,
" rgba " : [
0 ,
0 ,
255 ,
1
]
} ,
" color " : " blue " ,
" type " : " primary "
} ,
{
" category " : " hue " ,
" code " : {
" hex " : " # FF0 " ,
" rgba " : [
255 ,
255 ,
0 ,
1
]
} ,
" color " : " yellow " ,
" type " : " primary "
}
]
}
Giờ đây, cấu trúc của tệp JSON trở nên ít phức tạp hơn nhiều để nắm bắt. Bạn có thể sử dụng --sort-keys
tùy chọn để sắp xếp các khóa theo thứ tự bảng chữ cái.
Mặc dù thực hành tốt để phân chia tập lệnh và thư viện, Python có một thành ngữ giúp bạn có thể coi một mô-đun vừa là tập lệnh vừa là thư viện cùng một lúc. Như đã lưu ý trước đó , giá trị của __name__
biến mô-đun đặc biệt được đặt trong thời gian chạy dựa trên việc mô-đun được nhập hay chạy dưới dạng tập lệnh.
Hãy thử nghiệm nó ra ! Tạo tệp sau :
# name.py
print(__name__)
Nếu bạn chạy tệp này, thì bạn sẽ thấy tệp đó __name__
được đặt thành giá trị đặc biệt __main__
:
USDpython name.py
__main__
Tuy nhiên, nếu bạn nhập mô-đun, thì __name__
được đặt thành tên của mô-đun:
>> >
>> >import name
name
Hành vi này được tận dụng theo quy mô sau :
def main( ) :
...
if __name__ = = " __main__ ":
main( )
Hãy sử dụng điều này trong một ví dụ lớn hơn. Trong một nỗ lực để giữ cho bạn trẻ , tập lệnh sau sẽ thay thế bất kỳ độ tuổi “già” nào ( 25
hoặc cao hơn) bằng 24
:
1# feel_young. py
2
3def make_young(text) :
4 words = [replace_by_age(w) for w in text.split( ) ]
5 return " ".join(words)
6
7def replace_by_age(word, new_age=24, age_range=(25, 120) ) :
8 if word.isdigit( ) and int(word) in range(*age_range) :
9 return str(new_age)
10 return word
11
12if __name__ = = " __main__ ":
13 text = input(" Tell me something : ")
14 print(make_young(text) )
Bạn hoàn toàn có thể chạy tập lệnh này dưới dạng tập lệnh và nó sẽ tương tác làm cho độ tuổi bạn nhập trẻ hơn :
USDpython feel_young.py
Tell me something : Forever young - Bob is 79 years old
Forever young - Bob is 24 years old
Bạn cũng có thể sử dụng mô-đun như một thư viện có thể nhập. Bài if
kiểm tra trên dòng 12 đảm bảo rằng không có tác dụng phụ khi bạn nhập thư viện. Chỉ các chức năng make_young()
và replace_by_age()
được xác định. Ví dụ, bạn có thể sử dụng thư viện này như sau:
>> >
>> >from feel_young import make_young
>> >headline = " Twice As Many 100 - Year-Olds "
>> >make_young(headline)
' Twice As Many 24 - Year-Olds '
Nếu không có sự bảo vệ của if
bài kiểm tra, việc nhập sẽ kích hoạt tương tác input()
và feel_young
rất khó sử dụng làm thư viện.
Chạy tập lệnh Python từ tệp ZIP
Một tính năng hơi khó hiểu của Python là nó hoàn toàn có thể chạy những tập lệnh được đóng gói thành những tệp ZIP. Ưu điểm chính của việc này là bạn hoàn toàn có thể phân phối một gói khá đầy đủ dưới dạng một tệp duy nhất .
Tuy nhiên, quan tâm rằng điều này vẫn nhu yếu Python phải được setup trên mạng lưới hệ thống. Nếu bạn muốn phân phối ứng dụng Python của mình dưới dạng tệp thực thi độc lập, hãy xem Sử dụng PyInstaller để thuận tiện phân phối ứng dụng Python .
Nếu bạn cung cấp cho trình thông dịch Python một tệp ZIP , thì nó sẽ tìm kiếm một tệp có tên __main__.py
bên trong kho lưu trữ ZIP, giải nén nó và chạy nó. Như một ví dụ cơ bản, hãy tạo __main__.py
tệp sau :
# __main__. py
print(f" Hello from{__file__}")
Điều này sẽ in một thông tin khi bạn chạy nó :
USDpython __main__.py
Hello from __main__. py
Bây giờ hãy thêm nó vào kho tàng trữ ZIP. Bạn hoàn toàn có thể triển khai việc này trên dòng lệnh :
USDzip hello.zip __main__.py
adding : __main__. py ( stored 0 % )
Trên Windows, thay vào đó, bạn hoàn toàn có thể sử dụng trỏ và nhấp. Chọn tệp trong File Explorer, sau đó nhấp chuột phải và chọn Gửi đến → Thư mục nén ( nén ) .
Vì __main__
không phải là một cái tên mang tính mô tả nên bạn đã đặt tên cho tệp ZIP hello.zip
. Bây giờ bạn có thể gọi nó trực tiếp bằng Python:
USDpython hello.zip
Hello from hello.zip/__main__.py
Lưu ý rằng tập lệnh của bạn biết rằng nó nằm bên trong hello.zip
. Hơn nữa, gốc của tệp ZIP của bạn được thêm vào đường dẫn nhập của Python để các tập lệnh của bạn có thể nhập các mô-đun khác bên trong cùng một tệp ZIP.
Hãy nhớ lại ví dụ trước đó, trong đó bạn đã tạo một bài kiểm tra dựa trên dữ liệu dân số . Có thể phân phối toàn bộ ứng dụng này dưới dạng một tệp ZIP duy nhất. importlib.resources
sẽ đảm bảo tệp dữ liệu được trích xuất từ kho lưu trữ ZIP khi cần thiết.
Ứng dụng gồm có những tệp sau :
population_quiz/
│
├── data/
│ ├── __init__.py
│ └── WPP2019_TotalPopulationBySex.csv
│
└── population_quiz.py
Bạn có thể thêm chúng vào tệp ZIP theo cách tương tự như bạn đã làm ở trên. Tuy nhiên, Python đi kèm với một công cụ được gọi là zipapp
hợp lý hóa quá trình đóng gói các ứng dụng vào các kho lưu trữ ZIP. Bạn sử dụng nó như sau:
USDpython -m zipapp population_quiz -m population_quiz:main
Về cơ bản, lệnh này thực thi hai việc : nó tạo một điểm vào và đóng gói ứng dụng của bạn .
Hãy nhớ rằng bạn cần một __main__.py
tệp làm điểm nhập bên trong kho lưu trữ ZIP của mình. Nếu bạn cung cấp -m
tùy chọn với thông tin về cách khởi động ứng dụng của bạn, thì hãy zipapp
tạo tệp này cho bạn. Trong ví dụ này, tệp được tạo __main__.py
trông giống như sau:
# - * - coding : utf-8 - * -
import population_quiz
population_quiz.main( )
Điều này __main__.py
được đóng gói, cùng với nội dung của population_quiz
thư mục, vào một kho lưu trữ ZIP có tên population_quiz.pyz
. Các .pyz
tín hiệu hậu tố rằng đây là một tập tin Python quấn vào một kho lưu trữ ZIP.
Lưu ý: Theo mặc định, zipapp
không nén bất kỳ tệp nào. Nó chỉ gói chúng thành một tệp duy nhất. Bạn cũng có thể yêu zipapp
cầu nén các tệp bằng cách thêm -c
tùy chọn.
Tuy nhiên, tính năng này chỉ có sẵn trong Python 3.7 trở lên. Xem zipapp
tài liệu để biết thêm thông tin.
Trên Windows, .pyz
các tệp đã được đăng ký dưới dạng tệp Python. Trên Mac và Linux, bạn có thể zipapp
tạo các tệp thực thi bằng cách sử dụng -p
tùy chọn trình thông dịch và chỉ định trình thông dịch nào sẽ sử dụng:
USDpython -m zipapp population_quiz -m population_quiz:main \
> -p " / usr / bin / env python "
Các -p
tùy chọn bổ sung thêm một công việc ( #!
) mà nói với các hệ điều hành như thế nào để chạy các tập tin . Ngoài ra, nó làm cho .pyz
tệp thực thi được để bạn có thể chạy tệp chỉ bằng cách nhập tên của nó:
USD./population_quiz.pyz
Reading population data for 2020, Medium scenario
Question 1 :
1. Timor-Leste
2. Viet Nam
3. Bermuda
Which country has the largest population ?
Lưu ý ./
phía trước tên tệp. Đây là một thủ thuật điển hình trên Mac và Linux để chạy các tệp thực thi trong thư mục hiện tại. Nếu bạn di chuyển các tập tin vào một thư mục trên của bạn PATH
, hoặc nếu bạn đang sử dụng Windows, sau đó bạn sẽ có thể chỉ sử dụng tên tập tin: population_quiz.pyz
.
Lưu ý: Trên Python 3.6 trở lên, lệnh trước đó sẽ không thành công với thông báo rằng nó không thể tìm thấy tài nguyên dữ liệu dân số trong data
thư mục. Điều này là do một giới hạn trongzipimport
.
Một cách giải quyết là cung cấp đường dẫn tuyệt đối đến population_quiz.pyz
. Trên Mac và Linux, bạn có thể thực hiện việc này bằng thủ thuật sau:
USD`pwd`/population_quiz.pyz
Các pwd
lệnh mở rộng đến đường dẫn của thư mục hiện hành.
Hãy kết thúc phần này bằng cách xem một hiệu ứng đẹp của việc sử dụng importlib.resources
. Hãy nhớ rằng bạn đã sử dụng mã sau để mở tệp dữ liệu:
from importlib import resources
with resources.open_text(" data ", " WPP2019_TotalPopulationBySex. csv ") as fid:
...
Một cách phổ biến hơn để mở tệp dữ liệu là định vị chúng dựa trên __file__
thuộc tính của mô-đun của bạn :
import pathlib
DATA_DIR = pathlib.Path(__file__).parent / " data "
with open(DATA_DIR / " WPP2019_TotalPopulationBySex. csv ") as fid:
...
Cách tiếp cận này thường hoạt động giải trí tốt. Tuy nhiên, nó sẽ sụp đổ khi ứng dụng của bạn được đóng gói thành một tệp ZIP :
USDpython population_quiz.pyz
Reading population data for 2020, Medium scenario
Traceback ( most recent call last ) :
...
NotADirectoryError : ' population_quiz. pyz / data / WPP2019_TotalPopulationBySex. csv '
Tệp dữ liệu của bạn nằm trong kho lưu trữ ZIP, vì vậy bạn open()
không thể mở tệp đó. importlib.resources
mặt khác, sẽ trích xuất dữ liệu của bạn vào một tệp tạm thời trước khi mở nó.
Xử lý nhập khẩu theo chu kỳ
Nhập theo chu kỳ xảy ra khi bạn có hai hoặc nhiều mô-đun nhập lẫn nhau. Cụ thể hơn, hãy tưởng tượng rằng mô-đun yin
sử dụng import yang
và mô-đun yang
nhập tương tự yin
.
Hệ thống nhập của Python ở một mức độ nào đó được phong cách thiết kế để giải quyết và xử lý những quy trình nhập. Ví dụ, đoạn mã sau — mặc dầu không có ích lắm — chạy tốt :
# yin.py
print(f" Hello from yin ")
import yang
print(f" Goodbye from yin ")
# yang.py
print(f" Hello from yang ")
import yin
print(f" Goodbye from yang ")
Cố gắng nhập cũng như yin
nhập thông dịch viên tương tác yang
:
>> >
>> >import yin
Hello from yin
Hello from yang
Goodbye from yang
Goodbye from yin
Lưu ý rằng yang
được nhập vào giữa quá trình nhập yin
, chính xác tại import yang
câu lệnh trong mã nguồn của yin
. Lý do điều này không kết thúc trong đệ quy vô tận là người bạn cũ của chúng ta là bộ nhớ cache của mô-đun.
Khi bạn nhập import yin
, tham chiếu tới yin
sẽ được thêm vào bộ đệm ẩn của mô-đun ngay cả trước khi yin
được tải. Khi yang
cố gắng nhập yin
sau, nó chỉ cần sử dụng tham chiếu trong bộ đệm ẩn mô-đun.
Bạn cũng hoàn toàn có thể có những mô-đun làm điều gì đó có ích hơn một chút ít. Nếu bạn xác lập những thuộc tính và tính năng trong những mô-đun của mình, thì tổng thể vẫn hoạt động giải trí :
# yin.py
print(f" Hello from yin ")
import yang
number = 42
def combine( ) :
return number + yang.number
print(f" Goodbye from yin ")
# yang.py
print(f" Hello from yang ")
import yin
number = 24
def combine( ) :
return number + yin.number
print(f" Goodbye from yang ")
Việc nhập yin
hoạt động giống như trước đây:
>> >
>> >import yin
Hello from yin
Hello from yang
Goodbye from yang
Goodbye from yin
Các vấn đề liên quan đến nhập đệ quy bắt đầu xuất hiện khi bạn thực sự sử dụng mô-đun khác tại thời điểm nhập thay vì chỉ xác định các chức năng sẽ sử dụng mô-đun khác sau này. Thêm một dòng vào yang.py
:
# yin.py
print(f" Hello from yin ")
import yang
number = 42
def combine( ) :
return number + yang.number
print(f" Goodbye from yin ")
# yang.py
print(f" Hello from yang ")
import yin
number = 24
def combine( ) :
return number + yin.number
print(f" yin and yang combined is{combine( )}")
print(f" Goodbye from yang ")
Bây giờ Python bị nhầm lẫn bởi việc nhập :
>> >
>> >import yin
Hello from yin
Hello from yang
Traceback ( most recent call last ) :
...
File " ... / yang.py ", line 8, in combine
return number + yin.number
AttributeError: module ' yin ' has no attribute ' number '
Thông báo lỗi thoạt đầu có vẻ hơi khó hiểu. Nhìn lại mã nguồn, bạn có thể xác nhận rằng nó number
được xác định trong yin
mô-đun.
Vấn đề là nó number
không được xác định yin
tại thời điểm yang
được nhập. Do đó, yin.number
được sử dụng bởi cuộc gọi tới combine()
.
Để thêm vào sự nhầm lẫn, bạn sẽ không gặp vấn đề gì khi nhập yang
:
>> >
>> >import yang
Hello from yang
Hello from yin
Goodbye from yin
yin and yang combined is 66
Goodbye from yang
Theo thời gian yang
các cuộc gọi combine()
, yin
được nhập đầy đủ và yin.number
được xác định rõ. Bước cuối cùng, do bộ nhớ cache của mô-đun mà bạn đã thấy trước đó, import yin
có thể hoạt động nếu bạn thực hiện một số thao tác nhập khác trước:
>> >
>> >import yang
Hello from yang
Hello from yin
Goodbye from yin
yin and yang combined is 66
Goodbye from yang
>> >yin
Traceback ( most recent call last ) :
File ""
, line 1, in
NameError: name ' yin ' is not defined
>> >import yin
>> >yin.combine( )
66
Vậy làm thế nào để bạn hoàn toàn có thể tránh bị sa lầy và hoảng sợ trước việc nhập khẩu theo chu kỳ luân hồi ? Việc có hai hoặc nhiều mô-đun nhập lẫn nhau thường là một tín hiệu cho thấy bạn hoàn toàn có thể cải tổ phong cách thiết kế những mô-đun của mình .
Thông thường, thời gian thuận tiện nhất để sửa lỗi nhập theo chu kỳ luân hồi là trước khi bạn tiến hành chúng. Nếu bạn thấy những chu kỳ luân hồi trong bản phác thảo kiến trúc của mình, hãy xem kỹ hơn và nỗ lực phá vỡ những chu kỳ luân hồi .
Tuy nhiên, có những thời gian hài hòa và hợp lý để trình làng một chu kỳ luân hồi nhập khẩu. Như bạn đã thấy ở trên, đây không phải là yếu tố miễn là những mô-đun của bạn chỉ xác lập những thuộc tính, hàm, lớp, v.v. Mẹo thứ hai — cũng là một giải pháp phong cách thiết kế tốt — là giữ cho những mô-đun của bạn không có tính năng phụ tại thời gian nhập .
Nếu bạn thực sự cần những mô-đun có chu kỳ luân hồi nhập và những công dụng phụ, vẫn còn một cách khác : triển khai nhập cục bộ bên trong những tính năng của bạn .
Lưu ý rằng trong đoạn mã sau, import yang
được thực hiện bên trong combine()
. Điều này có hai hệ quả. Đầu tiên, yang
chỉ có sẵn bên trong combine()
chức năng. Quan trọng hơn, quá trình nhập sẽ không xảy ra cho đến khi bạn gọi combine()
sau khi yin
đã được nhập đầy đủ:
# yin.py
print(f" Hello from yin ")
number = 42
def combine( ) :
import yang
return number + yang.number
print(f" Goodbye from yin ")
# yang.py
print(f" Hello from yang ")
import yin
number = 24
def combine( ) :
return number + yin.number
print(f" yin and yang combined is{combine( )}")
print(f" Goodbye from yang ")
Bây giờ không có vấn đề gì khi nhập và sử dụng yin
:
>> >
>> >import yin
Hello from yin
Goodbye from yin
>> >yin.combine( )
Hello from yang
yin and yang combined is 66
Goodbye from yang
66
Lưu ý rằng yang
, trên thực tế, không được nhập cho đến khi bạn gọi combine()
. Để có góc nhìn khác về nhập khẩu theo chu kỳ, hãy xem ghi chú kinh điển của Fredrik Lundh .
Nhập hồ sơ
Một mối chăm sóc khi nhập một số ít mô-đun và gói là nó sẽ thêm vào thời hạn khởi động tập lệnh của bạn. Tùy thuộc vào ứng dụng của bạn, điều này hoàn toàn có thể quan trọng hoặc không .
Kể từ khi phát hành Python 3.7 , bạn đã có một cách nhanh chóng để biết cần bao nhiêu thời gian để nhập các gói và mô-đun. Python 3.7 hỗ trợ -X importtime
tùy chọn dòng lệnh, đo lường và in lượng thời gian mà mỗi mô-đun cần để nhập:
USDpython -X importtime -c " import datetime "
import time : self [ us ] | cumulative | imported package
...
import time : 87 | 87 | time
import time : 180 | 180 | math
import time : 234 | 234 | _datetime
import time : 820 | 1320 | datetime
Các cumulative
cột cho thấy thời gian tích lũy của nhập khẩu (trong micro) trên một cơ sở cho mỗi gói. Bạn có thể đọc danh sách như sau: Python dành 1320
micro để hoàn toàn nhập khẩu datetime
, trong đó liên quan đến nhập khẩu time
, math
và việc thực hiện C _datetime
là tốt.
Các self
chương trình cột thời gian nó đã nhập khẩu chỉ module nhất định, bao gồm bất kỳ nhập khẩu đệ quy. Bạn có thể thấy rằng time
mất 87
micro giây để nhập, math
lấy 180
, _datetime
lấy 234
và quá trình nhập datetime
chính nó mất 820
micro giây. Nói chung, điều này cộng lại thời gian tích lũy là 1320
micro giây (trong phạm vi sai số làm tròn).
Hãy xem countdown.py
ví dụ từ phần Colorama :
USDpython3.7 -X importtime countdown.py
import time : self [ us ] | cumulative | imported package
...
import time : 644 | 7368 | colorama. ansitowin32
import time : 310 | 11969 | colorama.initialise
import time : 333 | 12301 | colorama
import time : 297 | 12598 | optional_color
import time : 119 | 119 | time
Trong ví dụ này, quá trình nhập optional_color
mất gần 0,013 giây. Phần lớn thời gian đó được dành cho việc nhập khẩu Colorama và các phụ thuộc của nó. Các self
cột cho thấy thời gian nhập khẩu trừ nhập khẩu lồng nhau.
Đối với một ví dụ cực đoan, hãy xem xét population
singleton trước đó . Vì nó đang tải một tệp dữ liệu lớn nên quá trình nhập cực kỳ chậm. Để kiểm tra điều này, bạn có thể chạy import population
dưới dạng tập lệnh với -c
tùy chọn:
USDpython3.7 -X importtime -c " import population "
import time : self [ us ] | cumulative | imported package
...
import time : 4933 | 322111 | matplotlib.pyplot
import time : 1474 | 1474 | typing
import time : 420 | 1894 | importlib.resources
Reading population data for Medium scenario
import time : 1593774 | 1921024 | population
Trong trường hợp này, mất gần 2 giây để nhập population
, trong đó khoảng 1,6 giây được dành cho chính mô-đun, chủ yếu để tải tệp dữ liệu.
-X importtime
là một công cụ tuyệt vời để tối ưu hóa việc nhập của bạn. Nếu bạn cần thực hiện giám sát tổng quát hơn và tối ưu hóa mã của mình, thì hãy xem Chức năng hẹn giờ của Python: Ba cách để theo dõi mã của bạn .
Phần kết luận
Trong hướng dẫn này, bạn đã biết về mạng lưới hệ thống nhập Python. Giống như nhiều thứ trong Python, nó khá đơn thuần để sử dụng cho những tác vụ cơ bản như nhập những mô-đun và gói. Đồng thời, mạng lưới hệ thống nhập khẩu khá phức tạp, linh động và hoàn toàn có thể lan rộng ra. Bạn đã học được một số ít thủ pháp tương quan đến nhập mà bạn hoàn toàn có thể tận dụng trong mã của riêng mình .
Trong hướng dẫn này, bạn đã học cách:
- Tạo gói không gian tên
- Nhập tài nguyên và tệp dữ liệu
- Quyết định những gì sẽ nhập động trong thời gian chạy
- Mở rộng hệ thống nhập của Python
- Xử lý các phiên bản khác nhau của gói
Trong suốt hướng dẫn, bạn đã thấy nhiều link để cung ứng thêm thông tin. Nguồn có thẩm quyền nhất trên mạng lưới hệ thống nhập Python là tài liệu chính thức :
Bạn hoàn toàn có thể sử dụng kiến thức của mình về nhập Python bằng cách làm theo những ví dụ trong hướng dẫn này. Nhấp vào link bên dưới để truy vấn vào mã nguồn :
Lấy mã nguồn : Nhấp vào đây để lấy mã nguồn mà bạn sẽ sử dụng để tìm hiểu và khám phá về mạng lưới hệ thống nhập Python trong hướng dẫn này .
Source: https://final-blade.com
Category: Kiến thức Internet