Python đặt nhập khẩu ở đâu?

Chuyển đến nội dung chính

Giới thiệu về Python

Nắm vững kiến ​​thức cơ bản về phân tích dữ liệu với Python chỉ trong bốn giờ. Khóa học trực tuyến này sẽ giới thiệu giao diện Python và khám phá các gói phổ biến

Python trung cấp

Nâng cao kỹ năng khoa học dữ liệu của bạn bằng cách tạo trực quan hóa bằng Matplotlib và thao tác với DataFrames bằng gấu trúc

Có liên quan

Dữ liệu văn bản trong Python Cheat Sheet

Chào mừng bạn đến với bảng gian lận của chúng tôi để làm việc với dữ liệu văn bản trong Python. Chúng tôi đã biên soạn một danh sách các hàm và gói hữu ích nhất để dọn dẹp, xử lý và phân tích dữ liệu văn bản trong Python, cùng với các ví dụ và giải thích rõ ràng, vì vậy bạn sẽ có mọi thứ cần biết về cách làm việc với dữ liệu văn bản trong Python.

Hướng dẫn về tập hợp và lý thuyết tập hợp trong Python

Tìm hiểu về bộ Python. chúng là gì, cách tạo chúng, khi nào sử dụng chúng, các chức năng tích hợp và mối quan hệ của chúng với các hoạt động lý thuyết thiết lập

Hướng dẫn về gấu trúc. Khung dữ liệu trong Python

Khám phá phân tích dữ liệu với Python. Pandas DataFrames giúp thao tác dữ liệu của bạn dễ dàng, từ việc chọn hoặc thay thế các cột và chỉ mục để định hình lại dữ liệu của bạn

Xem ThêmXem Thêm

Nếu bạn hỏi tôi về khía cạnh dễ bị hiểu lầm nhất của Python, tôi sẽ trả lời mà không cần suy nghĩ kỹ. hệ thống nhập Python. Chỉ cần nhớ bạn đã sử dụng nhập khẩu tương đối bao nhiêu lần và nhận được thứ gì đó như

>>> __name__
'__main__'
5; . Mọi lập trình viên Python đều trải qua điều gì đó như thế này và các câu hỏi phổ biến về StackOverflow, chẳng hạn như chúng tôi Nhập tệp từ thư mục khác (1822 phiếu bầu), Nhập tương đối trong Python 3 (1064 phiếu) và Nhập tương đối lần thứ một tỷ (993 phiếu) là một chỉ báo tốt

Hệ thống nhập Python không có vẻ phức tạp – nó phức tạp. Vì vậy, mặc dù tài liệu thực sự tốt nhưng nó không cung cấp cho bạn bức tranh toàn cảnh về những gì đang diễn ra. Cách duy nhất để có được một bức tranh như vậy là nghiên cứu điều gì xảy ra đằng sau hậu trường khi Python thực thi một câu lệnh nhập. Và đó là những gì chúng ta sẽ làm ngày hôm nay

Ghi chú. Trong bài viết này tôi đang đề cập đến CPython 3. 9. Một số chi tiết triển khai chắc chắn sẽ thay đổi khi CPython phát triển. Tôi sẽ cố gắng theo dõi những thay đổi quan trọng và thêm ghi chú cập nhật

Kế hoạch của chúng tôi

Trước khi chúng tôi bắt đầu, hãy để tôi trình bày cho bạn một phiên bản chi tiết hơn về kế hoạch của chúng tôi. Đầu tiên, chúng ta sẽ thảo luận về các khái niệm cốt lõi của hệ thống nhập khẩu. mô-đun, mô-đun con, gói, câu lệnh

>>> __name__
'__main__'
7, nhập tương đối, v.v. Sau đó, chúng tôi sẽ loại bỏ các câu lệnh nhập khác nhau và thấy rằng cuối cùng tất cả chúng đều gọi hàm
>>> __name__
'__main__'
8 tích hợp. Cuối cùng, chúng ta sẽ nghiên cứu cách triển khai mặc định của
>>> __name__
'__main__'
8 hoạt động. Đi nào

Mô-đun và đối tượng mô-đun

Hãy xem xét một tuyên bố nhập khẩu đơn giản

import m

Bạn nghĩ nó làm gì? . Và bạn sẽ đúng. Nhưng mô-đun chính xác là gì? . câu lệnh

>>> import sys
>>> current_module = sys.modules[__name__] # sys.modules stores imported modules
>>> current_module.__dict__ is globals()
True
2 tìm kiếm một mô-đun có tên
>>> import sys
>>> current_module = sys.modules[__name__] # sys.modules stores imported modules
>>> current_module.__dict__ is globals()
True
0, tạo một đối tượng mô-đun cho mô-đun đó và gán đối tượng mô-đun cho biến. Xem cách chúng tôi phân biệt giữa mô-đun và đối tượng mô-đun. Bây giờ chúng ta có thể định nghĩa các thuật ngữ này

Mô-đun là bất kỳ thứ gì mà Python coi là mô-đun và biết cách tạo đối tượng mô-đun cho. Điều này bao gồm những thứ như tệp Python, thư mục và mô-đun tích hợp được viết bằng C. Chúng ta sẽ xem danh sách đầy đủ trong phần tiếp theo

Lý do tại sao chúng tôi nhập bất kỳ mô-đun nào là vì chúng tôi muốn có quyền truy cập vào các hàm, lớp, hằng số và các tên khác mà mô-đun định nghĩa. Những tên này phải được lưu trữ ở đâu đó và đây là đối tượng mô-đun dành cho. Đối tượng mô-đun là một đối tượng Python hoạt động như một không gian tên cho tên của mô-đun. Tên được lưu trữ trong từ điển của đối tượng mô-đun (có sẵn dưới dạng

>>> import sys
>>> current_module = sys.modules[__name__] # sys.modules stores imported modules
>>> current_module.__dict__ is globals()
True
4), vì vậy chúng tôi có thể truy cập chúng dưới dạng thuộc tính

Nếu bạn thắc mắc làm thế nào các đối tượng mô-đun được triển khai, thì đây là định nghĩa từ

>>> import sys
>>> current_module = sys.modules[__name__] # sys.modules stores imported modules
>>> current_module.__dict__ is globals()
True
5

>>> __name__
'__main__'
2

Trường

>>> import sys
>>> current_module = sys.modules[__name__] # sys.modules stores imported modules
>>> current_module.__dict__ is globals()
True
6 lưu trữ từ điển của mô-đun. Các lĩnh vực khác không thực sự quan trọng cho cuộc thảo luận của chúng tôi

Python tạo các đối tượng mô-đun ngầm cho chúng ta. Để thấy rằng không có gì kỳ diệu trong quá trình này, chúng ta hãy tự tạo một đối tượng mô-đun. Chúng ta thường tạo các đối tượng Python bằng cách gọi các kiểu của chúng, như

>>> import sys
>>> current_module = sys.modules[__name__] # sys.modules stores imported modules
>>> current_module.__dict__ is globals()
True
7 hoặc
>>> import sys
>>> current_module = sys.modules[__name__] # sys.modules stores imported modules
>>> current_module.__dict__ is globals()
True
8. Loại đối tượng mô-đun là
>>> import sys
>>> current_module = sys.modules[__name__] # sys.modules stores imported modules
>>> current_module.__dict__ is globals()
True
9 trong mã C nhưng nó không có sẵn trong Python dưới dạng tích hợp sẵn. May mắn thay, các loại "không khả dụng" như vậy có thể được tìm thấy trong mô-đun tiêu chuẩn
>>> __name__
'__main__'
70

>>> __name__
'__main__'
8

Mô-đun

>>> __name__
'__main__'
70 xác định
>>> __name__
'__main__'
72 như thế nào? . Chúng ta cũng có thể làm được

>>> __name__
'__main__'
3

Cho dù chúng ta có được

>>> __name__
'__main__'
72 bằng cách nào, một khi chúng ta có được nó, chúng ta có thể dễ dàng tạo một đối tượng mô-đun

>>> __name__
'__main__'
5

Một đối tượng mô-đun mới được tạo không thú vị lắm nhưng có một số thuộc tính đặc biệt được khởi tạo trước

>>> __name__
'__main__'
6

Hầu hết các thuộc tính đặc biệt này chủ yếu được sử dụng bởi chính hệ thống nhập khẩu, nhưng một số cũng được sử dụng trong mã ứng dụng. Ví dụ, thuộc tính

>>> __name__
'__main__'
76 thường được sử dụng để lấy tên của mô-đun hiện tại

>>> __name__
'__main__'

Lưu ý rằng

>>> __name__
'__main__'
76 có sẵn dưới dạng biến toàn cục. Quan sát này có vẻ hiển nhiên, nhưng nó rất quan trọng. Nó xuất phát từ thực tế là từ điển của các biến toàn cục được đặt thành từ điển của mô-đun hiện tại

>>> import sys
>>> current_module = sys.modules[__name__] # sys.modules stores imported modules
>>> current_module.__dict__ is globals()
True

Mô-đun hiện tại hoạt động như một không gian tên để thực thi mã Python. Khi Python nhập một tệp Python, nó sẽ tạo một đối tượng mô-đun mới và sau đó thực thi nội dung của tệp bằng cách sử dụng từ điển của đối tượng mô-đun làm từ điển của các biến toàn cục. Tương tự, khi Python thực thi trực tiếp một tệp Python, trước tiên, nó tạo một mô-đun đặc biệt có tên là

>>> __name__
'__main__'
78 và sau đó sử dụng từ điển của nó làm từ điển của các biến toàn cục. Do đó, các biến toàn cục luôn là thuộc tính của một số mô-đun và mô-đun này được coi là mô-đun hiện tại theo quan điểm của mã thực thi

Các loại mô-đun khác nhau

Theo mặc định, Python nhận ra những thứ sau đây là mô-đun

  1. Các mô-đun tích hợp
  2. mô-đun đông lạnh
  3. tiện ích mở rộng C
  4. Tệp mã nguồn Python (_______179 tệp)
  5. Tệp mã byte Python (tệp ______330)
  6. thư mục

Các mô-đun tích hợp là các mô-đun C được biên dịch thành tệp thực thi

>>> __name__
'__main__'
31. Vì chúng là một phần của tệp thực thi nên chúng luôn có sẵn. Đây là tính năng chính của họ. Bộ dữ liệu
>>> __name__
'__main__'
32 lưu trữ tên của họ

>>> __name__
'__main__'
7

Các mô-đun bị đóng băng cũng là một phần của tệp thực thi

>>> __name__
'__main__'
31, nhưng chúng được viết bằng Python. Mã Python được biên dịch thành một đối tượng mã và sau đó đối tượng mã sắp xếp lại được tích hợp vào tệp thực thi. Các ví dụ về các mô-đun bị đóng băng là
>>> __name__
'__main__'
34 và
>>> __name__
'__main__'
35. Python đóng băng chúng vì chúng triển khai cốt lõi của hệ thống nhập và do đó, không thể nhập như các tệp Python khác

Các tiện ích mở rộng của C hơi giống các mô-đun tích hợp và hơi giống các tệp Python. Một mặt, chúng được viết bằng C hoặc C++ và tương tác với Python thông qua API Python/C. Mặt khác, chúng không phải là một phần của tệp thực thi nhưng được tải động trong quá trình nhập. Một số mô-đun tiêu chuẩn bao gồm

>>> __name__
'__main__'
36,
>>> __name__
'__main__'
37 và
>>> __name__
'__main__'
38 là phần mở rộng của C. Nhiều người khác bao gồm
>>> __name__
'__main__'
39,
>>> __name__
'__main__'
200 và
>>> __name__
'__main__'
201 được viết bằng Python nhưng gọi phần mở rộng C dưới mui xe. Về mặt kỹ thuật, các tiện ích mở rộng C là các thư viện được chia sẻ hiển thị chức năng khởi tạo được gọi là. Chúng thường được đặt tên như
>>> __name__
'__main__'
202, nhưng phần mở rộng tệp có thể khác nhau tùy thuộc vào nền tảng. Ví dụ: trên macOS của tôi, bất kỳ tiện ích mở rộng nào trong số này sẽ hoạt động.
>>> __name__
'__main__'
203,
>>> __name__
'__main__'
204,
>>> __name__
'__main__'
205. Và trên Windows, bạn sẽ thấy
>>> __name__
'__main__'
206 và các biến thể của nó

Các tệp mã byte của Python thường nằm trong thư mục

>>> __name__
'__main__'
207 cùng với các tệp Python thông thường. Chúng là kết quả của việc biên dịch mã Python thành bytecode. Cụ thể hơn, tệp
>>> __name__
'__main__'
30 chứa một số siêu dữ liệu theo sau là đối tượng mã được sắp xếp theo thứ tự của một mô-đun. Mục đích của nó là giảm thời gian tải của mô-đun bằng cách bỏ qua giai đoạn biên dịch. Khi Python nhập tệp
>>> __name__
'__main__'
79, trước tiên, nó sẽ tìm kiếm tệp
>>> __name__
'__main__'
30 tương ứng trong thư mục
>>> __name__
'__main__'
207 và thực thi nó. Nếu tệp
>>> __name__
'__main__'
30 không tồn tại, Python sẽ biên dịch mã và tạo tệp

Tuy nhiên, chúng tôi sẽ không gọi các mô-đun tệp

>>> __name__
'__main__'
30 nếu chúng tôi không thể thực thi và nhập chúng trực tiếp. Ngạc nhiên thay, chúng ta có thể

>>> __name__
'__main__'
3

Để tìm hiểu thêm về các tệp

>>> __name__
'__main__'
30, hãy xem PEP 3147 -- PYC Repository Directories và PEP 552 -- Deterministic pycs

Như chúng ta sẽ thấy sau này, chúng ta có thể tùy chỉnh hệ thống nhập để hỗ trợ nhiều loại mô-đun hơn nữa. Vì vậy, mọi thứ đều có thể được gọi là mô-đun miễn là Python có thể tạo đối tượng mô-đun cho nó với tên mô-đun

Mô hình con và gói

Nếu tên mô-đun được giới hạn ở các mã định danh đơn giản như

>>> __name__
'__main__'
215 hoặc
>>> __name__
'__main__'
216, thì tất cả chúng phải là duy nhất và chúng tôi sẽ phải suy nghĩ rất nhiều mỗi khi đặt tên cho một tệp mới. Vì lý do này, Python cho phép các mô-đun có các mô-đun con và tên mô-đun chứa dấu chấm

Khi Python thực thi câu lệnh này

>>> __name__
'__main__'
20

đầu tiên nó nhập mô-đun

>>> __name__
'__main__'
217 và sau đó là mô-đun con
>>> __name__
'__main__'
218. Nó thêm mô-đun con vào từ điển của mô-đun và gán mô-đun cho biến
>>> __name__
'__main__'
217, vì vậy chúng ta có thể truy cập mô-đun con dưới dạng thuộc tính của mô-đun

Một mô-đun có thể có các mô-đun con được gọi là gói. Về mặt kỹ thuật, một gói là một mô-đun có thuộc tính

>>> __name__
'__main__'
220. Thuộc tính này cho Python biết nơi tìm kiếm các mô hình con. Khi Python nhập một mô-đun cấp cao nhất, nó sẽ tìm kiếm mô-đun đó trong các thư mục và kho lưu trữ ZIP được liệt kê trong
>>> __name__
'__main__'
6. Nhưng khi nó nhập một mô-đun con, nó sử dụng thuộc tính
>>> __name__
'__main__'
220 của mô-đun mẹ thay vì
>>> __name__
'__main__'
6

gói thông thường

Thư mục là cách phổ biến nhất để tổ chức các mô-đun thành các gói. Nếu một thư mục chứa tệp

>>> __name__
'__main__'
224, nó được coi là một gói thông thường. Khi Python nhập một thư mục như vậy, nó sẽ thực thi tệp
>>> __name__
'__main__'
224, vì vậy các tên được xác định ở đó sẽ trở thành các thuộc tính của mô-đun

Tệp

>>> __name__
'__main__'
224 thường để trống hoặc chứa các thuộc tính liên quan đến gói, chẳng hạn như
>>> __name__
'__main__'
227 và
>>> __name__
'__main__'
228. Nó cũng có thể được sử dụng để tách API công khai của gói khỏi triển khai nội bộ của gói đó. Giả sử bạn phát triển một thư viện với cấu trúc sau

>>> __name__
'__main__'
21

Và bạn muốn cung cấp cho người dùng thư viện của mình hai chức năng.

>>> __name__
'__main__'
229 được định nghĩa trong
>>> __name__
'__main__'
230 và
>>> __name__
'__main__'
231 được định nghĩa trong
>>> __name__
'__main__'
232. Nếu bạn để trống
>>> __name__
'__main__'
224, thì người dùng phải chỉ định các mô-đun con để nhập các chức năng

>>> __name__
'__main__'
22

Nó có thể là thứ bạn muốn, nhưng bạn cũng có thể muốn cho phép người dùng nhập các chức năng như thế này

>>> __name__
'__main__'
23

Vì vậy, bạn nhập các chức năng trong

>>> __name__
'__main__'
224

>>> __name__
'__main__'
24

Một thư mục có phần mở rộng C có tên là

>>> __name__
'__main__'
235 hoặc có tệp
>>> __name__
'__main__'
30 có tên là
>>> __name__
'__main__'
237 cũng là một gói thông thường. Python có thể nhập các gói như vậy hoàn toàn tốt

>>> __name__
'__main__'
25

Gói không gian tên

Trước phiên bản 3. 3, Python chỉ có các gói thông thường. Các thư mục không có

>>> __name__
'__main__'
224 hoàn toàn không được coi là gói. Và đây là một vấn đề vì mọi người không muốn tạo các tệp
>>> __name__
'__main__'
224 trống. PEP 420 khiến các tệp này trở nên không cần thiết bằng cách giới thiệu các gói không gian tên trong Python 3. 3

Các gói không gian tên cũng giải quyết một vấn đề khác. Họ cho phép các nhà phát triển đặt nội dung của một gói trên nhiều địa điểm. Ví dụ: nếu bạn có cấu trúc thư mục sau

>>> __name__
'__main__'
26

Và cả

>>> __name__
'__main__'
240 và
>>> __name__
'__main__'
241 đều ở trong
>>> __name__
'__main__'
6, sau đó bạn có thể nhập cả
>>> __name__
'__main__'
243 và
>>> __name__
'__main__'
244 như thế này

>>> __name__
'__main__'
27

Điều này là do

>>> __name__
'__main__'
245 là gói không gian tên chứa hai vị trí

>>> __name__
'__main__'
28

Làm thế nào nó hoạt động? . Nếu sau khi duyệt qua tất cả các mục, nó không thể tìm thấy gói thông thường, tệp Python hoặc phần mở rộng C, nó sẽ tạo một đối tượng mô-đun có

>>> __name__
'__main__'
220 chứa các thư mục đã ghi nhớ

Ý tưởng ban đầu về việc yêu cầu

>>> __name__
'__main__'
224 là để ngăn các thư mục có tên như
>>> __name__
'__main__'
251 hoặc
>>> __name__
'__main__'
252 che khuất các mô-đun tiêu chuẩn. Gói không gian tên không ẩn các mô-đun khác vì chúng có mức độ ưu tiên thấp hơn trong quá trình tìm kiếm mô-đun

Nhập khẩu từ các mô-đun

Bên cạnh việc nhập các mô-đun, chúng ta cũng có thể nhập các thuộc tính mô-đun bằng cách sử dụng câu lệnh

>>> __name__
'__main__'
7, như vậy

>>> __name__
'__main__'
29

Câu lệnh này nhập một mô-đun có tên

>>> __name__
'__main__'
254 và gán các thuộc tính đã chỉ định cho các biến tương ứng

>>> __name__
'__main__'
80

Lưu ý biến

>>> __name__
'__main__'
254 không có sau khi nhập coi như bị xóa

>>> __name__
'__main__'
81

Khi Python thấy rằng một mô-đun không có thuộc tính được chỉ định, nó coi thuộc tính đó là một mô-đun con và cố gắng nhập nó. Vì vậy, nếu

>>> __name__
'__main__'
254 định nghĩa
>>> __name__
'__main__'
257 và
>>> __name__
'__main__'
258 nhưng không phải là
>>> __name__
'__main__'
259, Python sẽ cố gắng nhập
>>> __name__
'__main__'
260

Nhập ký tự đại diện

Nếu chúng tôi không muốn chỉ định rõ ràng tên để nhập từ một mô-đun, chúng tôi có thể sử dụng hình thức nhập ký tự đại diện

>>> __name__
'__main__'
82

Tuyên bố này hoạt động như thể

>>> __name__
'__main__'
261 đã được thay thế bằng tất cả các tên công khai của mô-đun. Đây là những tên trong từ điển của mô-đun không bắt đầu bằng dấu gạch dưới
>>> __name__
'__main__'
262 hoặc tên được liệt kê trong thuộc tính
>>> __name__
'__main__'
263 nếu nó được xác định

Nhập khẩu tương đối

Cho đến bây giờ, chúng tôi đã nói với Python những mô-đun nào cần nhập bằng cách chỉ định tên mô-đun tuyệt đối. Câu lệnh

>>> __name__
'__main__'
7 cũng cho phép chúng tôi chỉ định tên mô-đun tương đối. Đây là vài ví dụ

>>> __name__
'__main__'
83

Các cấu trúc như

>>> __name__
'__main__'
265 và
>>> __name__
'__main__'
266 là các tên mô-đun tương đối, nhưng chúng tương đối với cái gì? . Mô-đun hiện tại, giống như bất kỳ mô-đun nào khác, có thể thuộc về một gói. Gói này được gọi là gói hiện tại và đây là tên mô-đun tương đối có liên quan đến

Thuộc tính

>>> __name__
'__main__'
267 của mô-đun lưu trữ tên của gói mà mô-đun thuộc về. Nếu mô-đun là một gói, thì mô-đun thuộc về chính nó và
>>> __name__
'__main__'
267 chỉ là tên của mô-đun (
>>> __name__
'__main__'
76). Nếu mô-đun là mô-đun con, thì nó thuộc về mô-đun chính và
>>> __name__
'__main__'
267 được đặt thành tên của mô-đun chính. Cuối cùng, nếu mô-đun không phải là gói cũng không phải là mô-đun con, thì gói của nó không được xác định. Trong trường hợp này,
>>> __name__
'__main__'
267 có thể được đặt thành một chuỗi rỗng (e. g. mô-đun là mô-đun cấp cao nhất) hoặc
>>> __name__
'__main__'
272 (e. g. mô-đun chạy dưới dạng tập lệnh)

Tên mô-đun tương đối là tên mô-đun đứng trước một số dấu chấm. Một dấu chấm hàng đầu đại diện cho gói hiện tại. Vì vậy, khi

>>> __name__
'__main__'
267 được xác định, câu lệnh sau

>>> __name__
'__main__'
84

hoạt động như thể dấu chấm đã được thay thế bằng giá trị của

>>> __name__
'__main__'
267

Mỗi dấu chấm thừa báo cho Python tăng một cấp từ

>>> __name__
'__main__'
267. Nếu
>>> __name__
'__main__'
267 được đặt thành
>>> __name__
'__main__'
277, thì câu lệnh này

>>> __name__
'__main__'
85

hoạt động như thể các dấu chấm đã được thay thế bằng

>>> __name__
'__main__'
217

Bạn không thể di chuyển ra ngoài gói cấp cao nhất. Nếu bạn thử cái này

>>> __name__
'__main__'
86

Python sẽ báo lỗi

>>> __name__
'__main__'
87

Điều này là do Python không di chuyển qua hệ thống tệp để giải quyết các lần nhập tương đối. Nó chỉ lấy giá trị của

>>> __name__
'__main__'
267, loại bỏ một số hậu tố và thêm một hậu tố mới để có tên mô-đun tuyệt đối

Rõ ràng, nhập khẩu tương đối bị phá vỡ khi

>>> __name__
'__main__'
267 hoàn toàn không được xác định. Trong trường hợp này, bạn gặp lỗi sau

>>> __name__
'__main__'
88

Bạn thường thấy nó nhất khi chạy chương trình có nhập tương đối dưới dạng tập lệnh. Vì bạn chỉ định chương trình nào sẽ chạy với đường dẫn hệ thống tệp chứ không phải với tên mô-đun và vì Python cần tên mô-đun để tính toán

>>> __name__
'__main__'
267, nên mã được thực thi trong mô-đun
>>> __name__
'__main__'
78 có thuộc tính
>>> __name__
'__main__'
267 được đặt thành
>>> __name__
'__main__'
272

Chạy chương trình dưới dạng mô-đun

Cách tiêu chuẩn để tránh lỗi nhập khi chạy chương trình có nhập tương đối là chạy chương trình dưới dạng mô-đun bằng cách sử dụng công tắc

>>> __name__
'__main__'
285

>>> __name__
'__main__'
89

Công tắc

>>> __name__
'__main__'
285 yêu cầu Python sử dụng cùng một cơ chế để tìm mô-đun như trong quá trình nhập. Python lấy tên mô-đun và có thể tính toán gói hiện tại. Ví dụ: nếu chúng tôi chạy mô-đun có tên
>>> __name__
'__main__'
287, trong đó
>>> __name__
'__main__'
254 đề cập đến tệp
>>> __name__
'__main__'
79 thông thường, thì mã sẽ được thực thi trong mô-đun
>>> __name__
'__main__'
78 có thuộc tính
>>> __name__
'__main__'
267 được đặt thành
>>> __name__
'__main__'
292. Bạn có thể đọc thêm về công tắc
>>> __name__
'__main__'
285 trong tài liệu và trong PEP 338

Ổn thỏa. Đây là một sự khởi động. Bây giờ chúng ta sẽ xem chính xác điều gì sẽ xảy ra khi chúng ta nhập một mô-đun

Giảm báo cáo nhập khẩu

Nếu chúng tôi loại bỏ bất kỳ câu lệnh nhập nào, chúng tôi sẽ thấy rằng cuối cùng nó sẽ gọi hàm

>>> __name__
'__main__'
8 tích hợp. Hàm này lấy một tên mô-đun và một loạt các tham số khác, tìm mô-đun và trả về một đối tượng mô-đun cho nó. Ít nhất, đây là những gì nó phải làm

Python cho phép chúng tôi đặt

>>> __name__
'__main__'
8 thành một chức năng tùy chỉnh, vì vậy chúng tôi có thể thay đổi hoàn toàn quy trình nhập. Ví dụ, đây là một thay đổi phá vỡ mọi thứ

>>> __name__
'__main__'
30

Bạn hiếm khi thấy mọi người ghi đè

>>> __name__
'__main__'
8 vì những lý do khác ngoài ghi nhật ký hoặc gỡ lỗi. Việc triển khai mặc định đã cung cấp các cơ chế mạnh mẽ để tùy chỉnh và chúng tôi sẽ chỉ tập trung vào nó

Việc triển khai mặc định của

>>> __name__
'__main__'
8 là
>>> __name__
'__main__'
298. Vâng, nó gần như đúng. Mô-đun
>>> __name__
'__main__'
299 là mô-đun tiêu chuẩn triển khai cốt lõi của hệ thống nhập. Nó được viết bằng Python vì quá trình nhập liên quan đến việc xử lý đường dẫn và những thứ khác mà bạn muốn làm bằng Python hơn là bằng C. Nhưng một số chức năng của
>>> __name__
'__main__'
299 được chuyển sang C vì lý do hiệu suất. Và mặc định
>>> __name__
'__main__'
8 thực sự gọi một cổng C của
>>> __name__
'__main__'
298. Đối với mục đích của chúng tôi, chúng tôi có thể bỏ qua sự khác biệt một cách an toàn và chỉ nghiên cứu phiên bản Python. Trước khi chúng tôi làm điều đó, hãy xem các câu lệnh nhập khác nhau gọi
>>> __name__
'__main__'
8 như thế nào

Nhập khẩu đơn giản

Nhớ lại rằng một đoạn mã Python được thực thi theo hai bước

  1. Trình biên dịch biên dịch mã thành bytecode
  2. VM thực thi bytecode

Để biết chức năng của câu lệnh nhập, chúng ta có thể xem mã byte được tạo cho nó và sau đó tìm hiểu xem mỗi lệnh mã byte thực hiện công việc gì bằng cách xem vòng đánh giá trong

>>> __name__
'__main__'
804

Để lấy mã byte, chúng tôi sử dụng mô-đun chuẩn

>>> __name__
'__main__'
805

>>> __name__
'__main__'
31

Lệnh

>>> __name__
'__main__'
806 đầu tiên đẩy
>>> __name__
'__main__'
807 lên ngăn xếp giá trị.
>>> __name__
'__main__'
806 thứ hai đẩy
>>> __name__
'__main__'
272. Sau đó, lệnh
>>> __name__
'__main__'
810 thực hiện điều gì đó mà chúng ta sẽ xem xét ngay sau đây. Cuối cùng,
>>> __name__
'__main__'
811 gán giá trị trên cùng của ngăn xếp giá trị cho biến
>>> import sys
>>> current_module = sys.modules[__name__] # sys.modules stores imported modules
>>> current_module.__dict__ is globals()
True
0

Đoạn mã thực thi lệnh

>>> __name__
'__main__'
810 như sau

>>> __name__
'__main__'
32

Tất cả các hành động xảy ra trong hàm

>>> __name__
'__main__'
814. Nó gọi
>>> __name__
'__main__'
8 để thực hiện công việc, nhưng nếu
>>> __name__
'__main__'
8 không bị ghi đè, nó sẽ sử dụng phím tắt và gọi cổng C của
>>> __name__
'__main__'
298 được gọi là
>>> __name__
'__main__'
818. Đây là cách logic này được triển khai trong mã

>>> __name__
'__main__'
33

Nếu bạn kiểm tra cẩn thận tất cả những điều trên, bạn sẽ có thể kết luận rằng tuyên bố này

import m

thực sự tương đương với mã này

>>> __name__
'__main__'
35

ý nghĩa của các đối số theo chuỗi tài liệu của

>>> __name__
'__main__'
298 như sau

>>> __name__
'__main__'
36

Như chúng tôi đã nói, tất cả các câu lệnh nhập cuối cùng đều gọi

>>> __name__
'__main__'
8. Họ khác nhau về những gì họ làm trước và sau cuộc gọi và cách họ thực hiện cuộc gọi. Ví dụ: nhập tương đối vượt qua giá trị khác 0 của
>>> __name__
'__main__'
821 và câu lệnh
>>> __name__
'__main__'
7 vượt qua giá trị không trống của
>>> __name__
'__main__'
823

Bây giờ hãy biểu diễn các câu lệnh nhập khác qua

>>> __name__
'__main__'
8 như chúng ta đã biểu thị
>>> import sys
>>> current_module = sys.modules[__name__] # sys.modules stores imported modules
>>> current_module.__dict__ is globals()
True
2 nhưng lần này nhanh hơn nhiều

Nhập mô-đun con

tuyên bố này

>>> __name__
'__main__'
37

biên dịch thành mã byte sau

>>> __name__
'__main__'
38

và tương đương với đoạn mã sau

>>> __name__
'__main__'
39

Các đối số của

>>> __name__
'__main__'
8 được truyền giống như trong trường hợp của
>>> import sys
>>> current_module = sys.modules[__name__] # sys.modules stores imported modules
>>> current_module.__dict__ is globals()
True
2. Điểm khác biệt duy nhất là VM gán kết quả của
>>> __name__
'__main__'
8 không phải cho tên của mô-đun (
>>> __name__
'__main__'
829 không phải là tên biến hợp lệ) mà cho mã định danh đầu tiên trước dấu chấm, i. e.
>>> __name__
'__main__'
217. Như chúng ta sẽ thấy,
>>> __name__
'__main__'
8 trả về mô-đun cấp cao nhất trong trường hợp này

từ nhập khẩu

tuyên bố này

>>> __name__
'__main__'
50

biên dịch thành mã byte sau

>>> __name__
'__main__'
51

và tương đương với đoạn mã sau

>>> __name__
'__main__'
52

Tên để nhập được thông qua là

>>> __name__
'__main__'
823. Khi
>>> __name__
'__main__'
823 không trống,
>>> __name__
'__main__'
8 không trả về mô-đun cấp cao nhất như trong trường hợp nhập đơn giản mà trả về mô-đun được chỉ định như
>>> __name__
'__main__'
218

từ nhập *

tuyên bố này

>>> __name__
'__main__'
53

biên dịch thành mã byte sau

>>> __name__
'__main__'
54

và tương đương với đoạn mã sau

>>> __name__
'__main__'
55

Thuộc tính

>>> __name__
'__main__'
263 liệt kê tất cả các tên công khai của mô-đun. Nếu một số tên được liệt kê trong
>>> __name__
'__main__'
263 không được xác định, thì
>>> __name__
'__main__'
8 sẽ cố gắng nhập chúng dưới dạng mô hình con

Nhập khẩu tương đối

tuyên bố này

>>> __name__
'__main__'
56

biên dịch thành mã byte sau

>>> __name__
'__main__'
57

và tương đương với đoạn mã sau

>>> __name__
'__main__'
58

Đối số

>>> __name__
'__main__'
821 cho biết
>>> __name__
'__main__'
8 có bao nhiêu dấu chấm hàng đầu mà nhập khẩu tương đối có. Vì nó được đặt thành
>>> __name__
'__main__'
841, nên
>>> __name__
'__main__'
8 tính toán tên tuyệt đối của mô-đun bằng cách (1) lấy giá trị của
>>> __name__
'__main__'
267 và (2) loại bỏ phần cuối cùng của nó. Thuộc tính
>>> __name__
'__main__'
267 có sẵn cho
>>> __name__
'__main__'
8 vì nó được thông qua với
>>> __name__
'__main__'
846

Bây giờ chúng ta đã hoàn thành các câu lệnh nhập và chỉ có thể tập trung vào hàm

>>> __name__
'__main__'
8

Bên trong __import__()

Khi tôi học cách chuẩn bị bài viết này, nghiên cứu

>>> __name__
'__main__'
8 bằng cách đi theo tất cả các đường dẫn mã của nó không phải là trải nghiệm thú vị nhất. Vì vậy, tôi cung cấp cho bạn một lựa chọn tốt hơn. Tôi sẽ tóm tắt các thuật toán chính của quy trình nhập bằng tiếng Anh đơn giản và cung cấp liên kết đến các chức năng triển khai các thuật toán này để bạn có thể đọc mã nếu có gì đó chưa rõ ràng

Thuật toán mà

>>> __name__
'__main__'
8 thực hiện có thể tóm tắt như sau

  1. Nếu
    >>> __name__
    '__main__'
    
    850, hãy phân giải tên mô-đun tương đối thành tên mô-đun tuyệt đối
  2. Nhập mô-đun
  3. Nếu
    >>> __name__
    '__main__'
    
    823 trống, hãy bỏ mọi thứ sau dấu chấm đầu tiên khỏi tên mô-đun để lấy tên của mô-đun cấp cao nhất. Nhập và trả lại mô-đun cấp cao nhất
  4. Nếu
    >>> __name__
    '__main__'
    
    823 chứa các tên không có trong từ điển của mô-đun, hãy nhập chúng dưới dạng mô-đun con. Nghĩa là, nếu
    >>> __name__
    '__main__'
    
    259 không có trong từ điển của mô-đun, hãy nhập
    >>> __name__
    '__main__'
    
    260. Nếu
    >>> __name__
    '__main__'
    
    261 nằm trong ________ 1823, hãy sử dụng mô-đun ________ 1263 làm
    >>> __name__
    '__main__'
    
    823 mới và lặp lại bước này
  5. Trả lại mô-đun

Bước 2 là nơi tất cả các hành động xảy ra. Chúng tôi sẽ tập trung vào nó trong các phần còn lại, nhưng trước tiên chúng ta hãy giải thích chi tiết về bước 1

Giải quyết họ hàng

Để giải quyết tên mô-đun tương đối,

>>> __name__
'__main__'
8 cần biết gói hiện tại của mô-đun mà từ đó câu lệnh nhập được thực thi. Vì vậy, nó tra cứu
>>> __name__
'__main__'
267 trong
>>> __name__
'__main__'
861. Nếu
>>> __name__
'__main__'
267 là
>>> __name__
'__main__'
272, thì
>>> __name__
'__main__'
8 sẽ cố gắng suy ra gói hiện tại từ
>>> __name__
'__main__'
76. Vì Python luôn đặt chính xác
>>> __name__
'__main__'
267 nên dự phòng này thường không cần thiết. Nó chỉ có thể hữu ích cho các mô-đun được tạo bằng các phương tiện khác với cơ chế nhập mặc định. Bạn có thể xem hàm
>>> __name__
'__main__'
867 để biết chính xác cách tính gói cước hiện tại. Tất cả những gì chúng ta nên nhớ là quá trình nhập tương đối bị ngắt khi
>>> __name__
'__main__'
267 được đặt thành một chuỗi trống, như trong trường hợp mô-đun cấp cao nhất hoặc thành
>>> __name__
'__main__'
272, như trong trường hợp tập lệnh và có cơ hội thành công nếu không. Hàm sau đảm bảo điều này

>>> __name__
'__main__'
59

Sau khi kiểm tra, tên họ hàng được giải quyết

>>> __name__
'__main__'
60

>>> __name__
'__main__'
8 gọi
>>> __name__
'__main__'
871 để nhập mô-đun

Quy trình nhập khẩu

Hàm

>>> __name__
'__main__'
871 lấy một tên mô-đun tuyệt đối và thực hiện các bước sau

  1. Nếu mô-đun ở trong
    >>> __name__
    '__main__'
    
    873, hãy trả lại
  2. Khởi tạo đường dẫn tìm kiếm mô-đun tới
    >>> __name__
    '__main__'
    
    272
  3. Nếu mô-đun có mô-đun mẹ (tên chứa ít nhất một dấu chấm), hãy nhập mô-đun mẹ nếu nó chưa có trong
    >>> __name__
    '__main__'
    
    873. Đặt đường dẫn tìm kiếm mô-đun thành
    >>> __name__
    '__main__'
    
    220 của cha mẹ
  4. Tìm thông số kỹ thuật của mô-đun bằng tên mô-đun và đường dẫn tìm kiếm mô-đun. Nếu không tìm thấy thông số kỹ thuật, hãy tăng
    >>> __name__
    '__main__'
    
    877
  5. Tải mô-đun từ thông số kỹ thuật
  6. Thêm mô-đun vào từ điển của mô-đun mẹ
  7. Trả lại mô-đun

Tất cả các mô-đun đã nhập được lưu trữ trong từ điển

>>> __name__
'__main__'
873. Từ điển này ánh xạ tên mô-đun tới các đối tượng mô-đun và hoạt động như một bộ đệm. Trước khi tìm kiếm mô-đun, ________ 1871 kiểm tra ________ 1873 và trả về mô-đun ngay lập tức nếu nó ở đó. Các mô-đun đã nhập được thêm vào
>>> __name__
'__main__'
881 ở cuối bước 5

Nếu mô-đun không có trong

>>> __name__
'__main__'
873, thì
>>> __name__
'__main__'
871 sẽ tiếp tục quá trình nhập. Quá trình này bao gồm tìm mô-đun và tải mô-đun. Trình tìm và trình tải là các đối tượng thực hiện các tác vụ này

Trình tìm và trình tải

Công việc của công cụ tìm là đảm bảo rằng mô-đun tồn tại, xác định trình tải nào sẽ được sử dụng để tải mô-đun và cung cấp thông tin cần thiết để tải, chẳng hạn như vị trí của mô-đun. Công việc của một trình tải là tạo một đối tượng mô-đun cho mô-đun và thực thi mô-đun. Cùng một đối tượng có thể hoạt động như một công cụ tìm và như một trình tải. Một đối tượng như vậy được gọi là một nhà nhập khẩu

Trình tìm kiếm triển khai phương thức

>>> __name__
'__main__'
884 lấy tên mô-đun và đường dẫn tìm kiếm mô-đun và trả về thông số mô-đun. Thông số mô-đun là một đối tượng đóng gói trình tải và tất cả thông tin cần thiết để tải. Điều này bao gồm các thuộc tính đặc biệt của mô-đun. Chúng được sao chép đơn giản từ thông số kỹ thuật sau khi đối tượng mô-đun được tạo. Ví dụ:
>>> __name__
'__main__'
220 được sao chép từ
>>> __name__
'__main__'
886 và
>>> __name__
'__main__'
267 được sao chép từ
>>> __name__
'__main__'
888. Xem tài liệu để biết danh sách đầy đủ các thuộc tính spec

Để tìm một thông số kỹ thuật,

>>> __name__
'__main__'
871 lặp lại các công cụ tìm được liệt kê trong
>>> __name__
'__main__'
890 và gọi
>>> __name__
'__main__'
884 trên từng công cụ cho đến khi tìm thấy thông số kỹ thuật. Nếu thông số kỹ thuật không được tìm thấy,
>>> __name__
'__main__'
871 tăng
>>> __name__
'__main__'
877

Theo mặc định,

>>> __name__
'__main__'
890 lưu trữ ba công cụ tìm

  1. >>> __name__
    '__main__'
    
    895 tìm kiếm các mô-đun tích hợp
  2. >>> __name__
    '__main__'
    
    896 tìm kiếm các mô-đun bị đóng băng;
  3. >>> __name__
    '__main__'
    
    897 tìm kiếm các loại mô-đun khác nhau bao gồm tệp Python, thư mục và tiện ích mở rộng C

Chúng được gọi là công cụ tìm đường dẫn meta. Python phân biệt chúng với các công cụ tìm mục nhập đường dẫn là một phần của

>>> __name__
'__main__'
897. Chúng ta sẽ thảo luận về cả hai loại công cụ tìm trong các phần tiếp theo

Sau khi tìm thấy thông số kỹ thuật,

>>> __name__
'__main__'
871 lấy trình tải từ thông số kỹ thuật và chuyển thông số kỹ thuật đó sang phương thức
>>> __name__
'__main__'
300 của trình tải để tạo một đối tượng mô-đun. Nếu
>>> __name__
'__main__'
300 không được triển khai hoặc trả về
>>> __name__
'__main__'
272, thì
>>> __name__
'__main__'
871 sẽ tự tạo đối tượng mô-đun mới. Nếu đối tượng mô-đun không xác định một số thuộc tính đặc biệt, thường là như vậy, các thuộc tính sẽ được sao chép từ thông số kỹ thuật. Đây là cách logic này được triển khai trong mã

>>> __name__
'__main__'
61

Sau khi tạo đối tượng mô-đun,

>>> __name__
'__main__'
871 thực thi mô-đun bằng cách gọi phương thức
>>> __name__
'__main__'
305 của trình tải. Phương thức này làm gì phụ thuộc vào trình tải, nhưng thông thường, nó sẽ đưa vào từ điển của mô-đun các hàm, lớp, hằng số và những thứ khác mà mô-đun định nghĩa. Ví dụ: trình tải tệp Python thực thi nội dung của tệp khi gọi ____
>>> __name__
'__main__'
305

Quá trình tải đầy đủ được thực hiện như sau

>>> __name__
'__main__'
62

Đoạn mã này thú vị vì nhiều lý do. Đầu tiên, một mô-đun được thêm vào

>>> __name__
'__main__'
873 trước khi nó được thực thi. Do logic này, Python hỗ trợ nhập vòng tròn. Nếu chúng ta có hai mô-đun nhập lẫn nhau như thế này

>>> __name__
'__main__'
63

>>> __name__
'__main__'
64

Chúng tôi có thể nhập chúng mà không gặp vấn đề gì

>>> __name__
'__main__'
65

Điều hấp dẫn là mô-đun

>>> __name__
'__main__'
217 chỉ được khởi tạo một phần khi mô-đun
>>> __name__
'__main__'
309 được thực thi. Vì vậy, nếu chúng ta sử dụng
>>> __name__
'__main__'
310 trong
>>> __name__
'__main__'
309

>>> __name__
'__main__'
66

chúng tôi nhận được một lỗi

>>> __name__
'__main__'
67

Thứ hai, một mô-đun bị xóa khỏi

>>> __name__
'__main__'
873 nếu quá trình thực thi không thành công vì bất kỳ lý do gì, nhưng các mô-đun đã được nhập thành công dưới dạng hiệu ứng phụ vẫn còn trong
>>> __name__
'__main__'
873

Cuối cùng, mô-đun trong

>>> __name__
'__main__'
873 có thể được thay thế trong quá trình thực thi mô-đun. Do đó, mô-đun được tra cứu trong
>>> __name__
'__main__'
873 trước khi nó được trả lại

Bây giờ chúng tôi đã hoàn thành với

>>> __name__
'__main__'
871 và
>>> __name__
'__main__'
8 và sẵn sàng để xem các công cụ tìm và trình tải khác nhau hoạt động như thế nào

BuildinImporter và FrozenImporter

Như chúng ta có thể đánh giá từ cái tên,

>>> __name__
'__main__'
895 vừa là công cụ tìm vừa là trình tải các mô-đun tích hợp. Phương thức
>>> __name__
'__main__'
884 của nó kiểm tra xem mô-đun có phải là mô-đun tích hợp hay không và nếu có, sẽ tạo một thông số kỹ thuật không chứa gì ngoài tên của mô-đun và trình tải. Phương thức
>>> __name__
'__main__'
300 của nó tìm hàm init của mô-đun và gọi nó. Cả hai phương pháp đều dễ thực hiện vì tên mô-đun tích hợp được ánh xạ tĩnh tới các hàm init

>>> __name__
'__main__'
68

Các hàm init là các hàm init giống như các phần mở rộng C xác định. Chúng tôi sẽ không thảo luận về cách chúng hoạt động ở đây, vì vậy nếu bạn muốn tìm hiểu thêm về điều này, hãy xem hướng dẫn Mở rộng Python với C hoặc C++

>>> __name__
'__main__'
896 tìm các mô-đun bị đóng băng theo cách tương tự. Tên của chúng được ánh xạ tĩnh tới các đối tượng mã

>>> __name__
'__main__'
69

Sự khác biệt với

>>> __name__
'__main__'
895 là
>>> __name__
'__main__'
300 trả về
>>> __name__
'__main__'
272. Các đối tượng mã được thực thi bởi
>>> __name__
'__main__'
305

Bây giờ chúng tôi tập trung vào công cụ tìm đường dẫn meta mà các nhà phát triển ứng dụng nên quan tâm nhất

Trình tìm đường dẫn

>>> __name__
'__main__'
897 tìm kiếm mô-đun trên đường dẫn tìm kiếm mô-đun. Đường dẫn tìm kiếm mô-đun là
>>> __name__
'__main__'
220 của cha mẹ được chuyển dưới dạng đối số
>>> __name__
'__main__'
328 thành
>>> __name__
'__main__'
884 hoặc
>>> __name__
'__main__'
6 nếu đối số này là
>>> __name__
'__main__'
272. Nó được mong đợi là một chuỗi có thể lặp lại. Mỗi chuỗi, được gọi là mục nhập đường dẫn, phải chỉ định vị trí để tìm kiếm mô-đun, chẳng hạn như thư mục trên hệ thống tệp

>>> __name__
'__main__'
897 không thực sự tự tìm kiếm nhưng liên kết từng mục nhập đường dẫn với một công cụ tìm mục nhập đường dẫn biết cách tìm các mô-đun ở vị trí được chỉ định bởi mục nhập đường dẫn. Để tìm một mô-đun,
>>> __name__
'__main__'
897 lặp lại các mục nhập đường dẫn và đối với mỗi mục nhập, hãy gọi
>>> __name__
'__main__'
884 của công cụ tìm mục nhập đường dẫn tương ứng

Để biết nên sử dụng công cụ tìm mục nhập đường dẫn nào cho một mục nhập cụ thể,

>>> __name__
'__main__'
897 gọi các hook đường dẫn được liệt kê trong
>>> __name__
'__main__'
336. Móc đường dẫn là một hàm có thể gọi được lấy mục nhập đường dẫn và trả về công cụ tìm mục nhập đường dẫn. Nó cũng có thể nâng
>>> __name__
'__main__'
337, trong trường hợp đó,
>>> __name__
'__main__'
897 sẽ thử móc tiếp theo. Để tránh gọi hook trong mỗi lần nhập,
>>> __name__
'__main__'
897 lưu trữ các kết quả trong từ điển
>>> __name__
'__main__'
340 để ánh xạ các mục nhập đường dẫn tới các công cụ tìm mục nhập đường dẫn

Theo mặc định,

>>> __name__
'__main__'
336 chứa hai móc đường dẫn

  1. một hook trả về các phiên bản
    >>> __name__
    '__main__'
    
    342;
  2. một hook trả về các phiên bản
    >>> __name__
    '__main__'
    
    343

Phiên bản

>>> __name__
'__main__'
342 tìm kiếm các mô-đun trong kho lưu trữ ZIP hoặc trong một thư mục bên trong kho lưu trữ ZIP. Nó hỗ trợ các loại mô-đun giống như
>>> __name__
'__main__'
343 ngoại trừ phần mở rộng C. Bạn có thể đọc thêm về
>>> __name__
'__main__'
342 trong tài liệu và trong PEP 273. Phiên bản
>>> __name__
'__main__'
343 tìm kiếm các mô-đun trong một thư mục. Chúng ta sẽ thảo luận về nó trong phần tiếp theo

Bên cạnh việc gọi các công cụ tìm mục nhập đường dẫn,

>>> __name__
'__main__'
897 tạo thông số kỹ thuật cho các gói không gian tên. Khi một công cụ tìm mục nhập đường dẫn trả về một thông số không chỉ định trình tải, điều này có nghĩa là thông số đó mô tả một phần của gói không gian tên (thường chỉ là một thư mục). Trong trường hợp này,
>>> __name__
'__main__'
897 nhớ thuộc tính
>>> __name__
'__main__'
350 của thông số kỹ thuật này và tiếp tục với mục nhập đường dẫn tiếp theo với hy vọng rằng nó sẽ tìm thấy tệp Python, gói thông thường hoặc tiện ích mở rộng C. Nếu cuối cùng nó không tìm thấy bất kỳ phần nào trong số này, thì nó sẽ tạo một thông số kỹ thuật mới cho gói không gian tên có
>>> __name__
'__main__'
350 chứa tất cả các phần đã ghi nhớ

Tóm lại những gì chúng ta đã nói về

>>> __name__
'__main__'
897, đây là thuật toán hoàn chỉnh mà
>>> __name__
'__main__'
884 của nó thực hiện

  1. Nếu
    >>> __name__
    '__main__'
    
    328 là
    >>> __name__
    '__main__'
    
    272, đặt
    >>> __name__
    '__main__'
    
    328 thành
    >>> __name__
    '__main__'
    
    6
  2. Khởi tạo danh sách các mục nhập đường dẫn của gói không gian tên tiềm năng.
    >>> __name__
    '__main__'
    
    358
  3. Đối với mỗi mục nhập đường dẫn trong
    >>> __name__
    '__main__'
    
    328
    1. Tra cứu mục nhập trong
      >>> __name__
      '__main__'
      
      340 để có công cụ tìm mục nhập đường dẫn
    2. Nếu mục nhập không có trong
      >>> __name__
      '__main__'
      
      340, hãy gọi các hook được liệt kê trong
      >>> __name__
      '__main__'
      
      336 cho đến khi một số hook trả về công cụ tìm mục nhập đường dẫn
    3. Lưu trữ công cụ tìm mục nhập đường dẫn trong
      >>> __name__
      '__main__'
      
      340. Nếu không tìm thấy công cụ tìm mục nhập đường dẫn nào, hãy lưu trữ
      >>> __name__
      '__main__'
      
      272 và tiếp tục với mục nhập tiếp theo
    4. Gọi
      >>> __name__
      '__main__'
      
      884 của công cụ tìm mục nhập đường dẫn. Nếu thông số kỹ thuật là
      >>> __name__
      '__main__'
      
      272, hãy tiếp tục với mục tiếp theo
    5. Nếu tìm thấy gói không gian tên (
      >>> __name__
      '__main__'
      
      367 là
      >>> __name__
      '__main__'
      
      272), hãy mở rộng
      >>> __name__
      '__main__'
      
      369 bằng
      >>> __name__
      '__main__'
      
      886 và tiếp tục với mục tiếp theo
    6. Nếu không, hãy trả lại thông số kỹ thuật
  4. Nếu
    >>> __name__
    '__main__'
    
    369 trống, trả về
    >>> __name__
    '__main__'
    
    272
  5. Tạo thông số gói không gian tên mới với
    >>> __name__
    '__main__'
    
    350 dựa trên
    >>> __name__
    '__main__'
    
    369
  6. Trả lại thông số kỹ thuật

Tất cả logic phức tạp này của

>>> __name__
'__main__'
897 hầu như không cần thiết. Thông thường, một mục nhập đường dẫn chỉ là một đường dẫn đến một thư mục, vì vậy,
>>> __name__
'__main__'
897 gọi phương thức
>>> __name__
'__main__'
884 của một thể hiện
>>> __name__
'__main__'
343 được trả về bởi hook tương ứng

Trình tìm tệp

Phiên bản

>>> __name__
'__main__'
343 tìm kiếm các mô-đun trong thư mục được chỉ định bởi mục nhập đường dẫn. Mục nhập đường dẫn có thể là đường dẫn tuyệt đối hoặc đường dẫn tương đối. Trong trường hợp sau, nó được giải quyết đối với thư mục làm việc hiện tại

Phương pháp

>>> __name__
'__main__'
884 của
>>> __name__
'__main__'
343 lấy tên mô-đun tuyệt đối nhưng chỉ cần phần "đuôi" sau dấu chấm cuối cùng vì phần gói đã được sử dụng để xác định thư mục cần tìm trong. Nó trích xuất "đuôi" như thế này

>>> __name__
'__main__'
0

Sau đó, nó thực hiện tìm kiếm. Nó tìm kiếm một thư mục có tên là

>>> __name__
'__main__'
382 chứa
>>> __name__
'__main__'
224,
>>> __name__
'__main__'
237 hoặc
>>> __name__
'__main__'
385 với một số phần mở rộng tệp thư viện được chia sẻ như
>>> __name__
'__main__'
205. Nó cũng tìm các tệp có tên
>>> __name__
'__main__'
387,
>>> __name__
'__main__'
388 và
>>> __name__
'__main__'
389. Nếu nó tìm thấy bất kỳ thứ nào trong số này, nó sẽ tạo một thông số kỹ thuật với trình tải tương ứng

  • >>> __name__
    '__main__'
    
    390 cho tiện ích mở rộng C
  • >>> __name__
    '__main__'
    
    391 cho tệp
    >>> __name__
    '__main__'
    
    79;
  • >>> __name__
    '__main__'
    
    393 cho tệp
    >>> __name__
    '__main__'
    
    30

Nếu nó tìm thấy một thư mục không phải là gói thông thường, nó sẽ tạo một thông số kỹ thuật với bộ tải được đặt thành

>>> __name__
'__main__'
272.
>>> __name__
'__main__'
897 thu thập một thông số gói không gian tên duy nhất từ ​​các thông số kỹ thuật đó

Thuật toán mà

>>> __name__
'__main__'
884 thực hiện có thể được tóm tắt như sau

  1. Lấy phần cuối cùng của tên mô-đun.
    >>> __name__
    '__main__'
    
    398
  2. Tìm thư mục có tên
    >>> __name__
    '__main__'
    
    382 chứa
    >>> __name__
    '__main__'
    
    500. Nếu tìm thấy, hãy tạo và trả lại thông số gói thông thường
  3. Tìm tệp có tên
    >>> __name__
    '__main__'
    
    389 Nếu tìm thấy, hãy tạo và trả lại thông số tệp
  4. Lặp lại bước 2 và 3 cho các tệp
    >>> __name__
    '__main__'
    
    79 và cho các tệp
    >>> __name__
    '__main__'
    
    30
  5. Nếu tìm thấy thư mục có tên
    >>> __name__
    '__main__'
    
    382 không phải là gói thông thường, hãy tạo và trả về thông số gói không gian tên
  6. Nếu không, hãy trả lại
    >>> __name__
    '__main__'
    
    272

Một thông số gói thông thường được tạo như thế này

>>> __name__
'__main__'
1

một thông số tập tin như thế này

>>> __name__
'__main__'
2

và một gói không gian tên như thế này

>>> __name__
'__main__'
3

Khi thông số kỹ thuật được tạo, quá trình tải mô-đun bắt đầu.

>>> __name__
'__main__'
390 đáng để nghiên cứu, nhưng chúng ta nên để dành nó cho một bài viết khác về tiện ích mở rộng C.
>>> __name__
'__main__'
393 cũng không thú vị lắm nên cũng không bàn nữa.
>>> __name__
'__main__'
391 phù hợp nhất với chúng tôi vì nó tải các tệp
>>> __name__
'__main__'
79. Chúng tôi sẽ đề cập ngắn gọn về cách thức hoạt động của nó

NguồnFileLoader

Phương thức

>>> __name__
'__main__'
300 của
>>> __name__
'__main__'
391 luôn trả về
>>> __name__
'__main__'
272. Điều này có nghĩa là
>>> __name__
'__main__'
871 tự tạo đối tượng mô-đun mới và khởi tạo nó bằng cách sao chép các thuộc tính từ thông số kỹ thuật

Phương pháp

>>> __name__
'__main__'
305 của
>>> __name__
'__main__'
391 thực hiện chính xác những gì bạn mong đợi

>>> __name__
'__main__'
4

Nó gọi

>>> __name__
'__main__'
516 để tạo một đối tượng mã từ tệp và sau đó gọi
>>> __name__
'__main__'
517 để thực thi đối tượng mã trong không gian tên của mô-đun. Lưu ý rằng trước tiên,
>>> __name__
'__main__'
516 sẽ cố đọc mã byte từ tệp
>>> __name__
'__main__'
30 trong thư mục
>>> __name__
'__main__'
207 và tạo tệp này nếu nó chưa tồn tại

Đó là nó. Chúng tôi đã hoàn thành nghiên cứu của mình về công cụ tìm và trình tải và xem điều gì xảy ra trong quá trình nhập. Hãy tóm tắt những gì chúng ta đã học

Tóm tắt quy trình nhập khẩu

Bất kỳ câu lệnh nhập nào cũng biên dịch thành một loạt hướng dẫn mã byte, một trong số đó, được gọi là

>>> __name__
'__main__'
810, nhập mô-đun bằng cách gọi hàm
>>> __name__
'__main__'
8 tích hợp. Nếu mô-đun được chỉ định bằng tên tương đối, trước tiên,
>>> __name__
'__main__'
8 sẽ phân giải tên tương đối thành tên tuyệt đối bằng cách sử dụng thuộc tính
>>> __name__
'__main__'
267 của mô-đun hiện tại. Sau đó, nó tra cứu mô-đun trong
>>> __name__
'__main__'
873 và trả về mô-đun nếu nó ở đó. Nếu mô-đun không có ở đó,
>>> __name__
'__main__'
8 sẽ cố gắng tìm thông số kỹ thuật của mô-đun. Nó gọi phương thức
>>> __name__
'__main__'
884 của mọi công cụ tìm được liệt kê trong
>>> __name__
'__main__'
890 cho đến khi một số công cụ tìm trả về thông số kỹ thuật. Nếu mô-đun là mô-đun tích hợp,
>>> __name__
'__main__'
895 trả về thông số kỹ thuật. Nếu mô-đun là mô-đun bị đóng băng,
>>> __name__
'__main__'
896 trả về thông số kỹ thuật. Mặt khác,
>>> __name__
'__main__'
897 tìm kiếm mô-đun trên đường dẫn tìm kiếm mô-đun, đó là thuộc tính
>>> __name__
'__main__'
220 của mô-đun chính hoặc
>>> __name__
'__main__'
6 nếu cái trước không được xác định.
>>> __name__
'__main__'
897 lặp lại các mục nhập đường dẫn và, đối với mỗi mục nhập, gọi phương thức
>>> __name__
'__main__'
884 của công cụ tìm mục nhập đường dẫn tương ứng. Để có công cụ tìm mục nhập đường dẫn tương ứng,
>>> __name__
'__main__'
897 chuyển mục nhập đường dẫn tới các nội dung có thể gọi được liệt kê trong
>>> __name__
'__main__'
336. Nếu mục nhập đường dẫn là một đường dẫn đến một thư mục, thì một trong các hàm có thể gọi được sẽ trả về một phiên bản
>>> __name__
'__main__'
343 để tìm kiếm các mô-đun trong thư mục đó.
>>> __name__
'__main__'
897 gọi nó là
>>> __name__
'__main__'
884. Phương pháp
>>> __name__
'__main__'
884 của
>>> __name__
'__main__'
343 kiểm tra xem thư mục được chỉ định bởi mục nhập đường dẫn có chứa phần mở rộng C, tệp
>>> __name__
'__main__'
79, tệp
>>> __name__
'__main__'
30 hoặc thư mục có tên khớp với tên mô-đun hay không. Nếu nó tìm thấy bất cứ thứ gì, nó sẽ tạo một thông số kỹ thuật mô-đun với trình tải tương ứng. Khi
>>> __name__
'__main__'
8 nhận thông số kỹ thuật, nó gọi phương thức
>>> __name__
'__main__'
300 của trình tải để tạo một đối tượng mô-đun và sau đó gọi phương thức
>>> __name__
'__main__'
305 để thực thi mô-đun. Cuối cùng, nó đặt mô-đun vào
>>> __name__
'__main__'
873 và trả về mô-đun

Bạn còn câu hỏi nào không?

Có gì trong hệ thống. con đường?

Theo mặc định,

>>> __name__
'__main__'
6 bao gồm những điều sau đây

  1. Một thư mục hiện tại phụ thuộc vào lời gọi. Nếu bạn chạy một chương trình dưới dạng tập lệnh, thì đó là thư mục chứa tập lệnh. Nếu bạn chạy một chương trình dưới dạng một mô-đun bằng cách sử dụng công tắc
    >>> __name__
    '__main__'
    
    285, thì đó là thư mục mà bạn chạy tệp thực thi
    >>> __name__
    '__main__'
    
    31 từ đó. Nếu bạn chạy
    >>> __name__
    '__main__'
    
    31 ở chế độ tương tác hoặc thực hiện lệnh bằng cách sử dụng khóa chuyển đổi
    >>> __name__
    '__main__'
    
    553, thì mục nhập đầu tiên trong
    >>> __name__
    '__main__'
    
    6 sẽ là một chuỗi trống
  2. Các thư mục được chỉ định bởi biến môi trường
    >>> __name__
    '__main__'
    
    555
  3. Một kho lưu trữ zip có chứa thư viện tiêu chuẩn, e. g.
    >>> __name__
    '__main__'
    
    556. Nó được sử dụng để cài đặt nhúng. Cài đặt bình thường không bao gồm kho lưu trữ này
  4. Một thư mục chứa các mô-đun chuẩn được viết bằng Python, e. g.
    >>> __name__
    '__main__'
    
    557
  5. Một thư mục chứa các phần mở rộng C tiêu chuẩn, e. g.
    >>> __name__
    '__main__'
    
    558
  6. Các thư mục dành riêng cho trang web được thêm vào bởi mô-đun
    >>> __name__
    '__main__'
    
    252, e. g.
    >>> __name__
    '__main__'
    
    560. Đó là nơi các mô-đun của bên thứ ba được cài đặt bởi các công cụ như
    >>> __name__
    '__main__'
    
    561 hoạt động

Để xây dựng các đường dẫn này, trước tiên Python xác định vị trí của tệp thực thi

>>> __name__
'__main__'
31. Nếu chúng ta chạy tệp thực thi bằng cách chỉ định một đường dẫn, Python đã biết vị trí. Nếu không, nó sẽ tìm kiếm tệp thực thi trong
>>> __name__
'__main__'
563. Cuối cùng, nó nhận được một cái gì đó như
>>> __name__
'__main__'
564. Sau đó, nó cố gắng tìm ra vị trí của các mô-đun tiêu chuẩn. Nó di chuyển một thư mục lên từ tệp thực thi cho đến khi tìm thấy tệp
>>> __name__
'__main__'
565. Tệp này biểu thị thư mục chứa các mô-đun chuẩn được viết bằng Python. Quá trình tương tự được lặp lại để tìm thư mục chứa các phần mở rộng C tiêu chuẩn, nhưng thư mục
>>> __name__
'__main__'
566 được sử dụng làm điểm đánh dấu lần này. Tệp
>>> __name__
'__main__'
567 cùng với tệp thực thi hoặc một thư mục phía trên có thể chỉ định một thư mục khác để bắt đầu tìm kiếm từ đó. Và biến môi trường
>>> __name__
'__main__'
568 có thể được sử dụng để chỉ định thư mục "cơ sở" để Python hoàn toàn không cần thực hiện tìm kiếm

Mô-đun tiêu chuẩn

>>> __name__
'__main__'
252 lấy thư mục "cơ sở" được tìm thấy trong quá trình tìm kiếm hoặc được chỉ định bởi
>>> __name__
'__main__'
568 và thêm
>>> __name__
'__main__'
571 vào thư mục đó để lấy thư mục chứa các mô-đun của bên thứ ba. Thư mục này có thể chứa các tệp cấu hình đường dẫn
>>> __name__
'__main__'
572 yêu cầu
>>> __name__
'__main__'
252 thêm các thư mục dành riêng cho trang web vào
>>> __name__
'__main__'
6. Các thư mục được thêm vào cũng có thể chứa các tệp
>>> __name__
'__main__'
572 để quá trình lặp lại theo cách đệ quy

Nếu tệp

>>> __name__
'__main__'
567 tồn tại, thì
>>> __name__
'__main__'
252 sử dụng thư mục chứa tệp này làm thư mục "cơ sở". Lưu ý rằng đây không phải là thư mục mà
>>> __name__
'__main__'
567 chỉ định. Theo cơ chế này, Python hỗ trợ các môi trường ảo có các thư mục dành riêng cho trang web nhưng chia sẻ thư viện chuẩn với cài đặt toàn hệ thống. Kiểm tra các tài liệu trên
>>> __name__
'__main__'
252 và PEP 405 - Môi trường ảo Python để tìm hiểu thêm về điều này

Quá trình tính toán

>>> __name__
'__main__'
6 thậm chí còn phức tạp hơn. Nếu bạn muốn biết những sắc thái đó, hãy xem câu trả lời StackOverflow này

Phần kết luận

Nếu bạn hỏi tôi về khía cạnh dễ bị hiểu lầm nhất của Python, tôi sẽ trả lời mà không cần suy nghĩ kỹ. hệ thống nhập Python. Cho đến khi tôi viết bài này, tôi thực sự không thể nói chính xác mô-đun là gì; . Tôi có thể nói gì bây giờ? . Tôi đổ lỗi cho sự hiểu lầm của mình về các tài liệu đã đơn giản hóa thực tế như thế này

Một mô-đun là một tệp chứa các định nghĩa và câu lệnh Python

hoặc bỏ qua các chi tiết như thế này

Bạn có thể coi các gói là các thư mục trên một hệ thống tệp và các mô-đun là các tệp trong các thư mục, nhưng đừng hiểu sự tương tự này theo nghĩa đen vì các gói và mô-đun không cần bắt nguồn từ hệ thống tệp. Đối với mục đích của tài liệu này, chúng tôi sẽ sử dụng sự tương tự thuận tiện này của các thư mục và tệp

Nhập khẩu tương đối thực sự không trực quan, nhưng một khi bạn hiểu rằng chúng chỉ là một cách để chỉ định tên mô-đun liên quan đến tên gói hiện tại, bạn sẽ không gặp vấn đề gì với chúng

Công cụ tìm đường dẫn meta, công cụ tìm mục nhập đường dẫn, móc đường dẫn, mục nhập đường dẫn và trình tải làm cho hệ thống nhập phức tạp hơn nhưng cũng làm cho nó linh hoạt hơn. PEP 302 và PEP 451 đưa ra một số lý do cho sự đánh đổi này

Còn về

>>> __name__
'__main__'
6 thì sao? . Có lẽ, nó quá phức tạp để mô tả chính xác. Nhưng tôi nghĩ rằng giá trị gần đúng như giá trị chúng tôi đưa ra trong phần trước là đủ tốt cho các mục đích thực tế

Nhìn chung, nghiên cứu hệ thống nhập khẩu là hữu ích, nhưng tôi nghĩ rằng lần sau chúng ta nên nghiên cứu một cái gì đó thú vị hơn. Làm thế nào về không đồng bộ/chờ đợi?


Nếu bạn có bất kỳ câu hỏi, nhận xét hoặc đề xuất nào, vui lòng liên hệ với tôi theo địa chỉ victor@tenthousandmeters. com

Python tìm hàng nhập khẩu ở đâu?

Trình thông dịch python cố tìm thư mục chứa mô-đun mà chúng tôi đang cố gắng nhập vào sys. đường dẫn . Đó là danh sách các thư mục mà Python sẽ tìm kiếm sau khi tìm kiếm xong các mô-đun được lưu trong bộ nhớ cache và các mô-đun thư viện chuẩn của Python.

Điều gì xảy ra khi nhập Python?

Khi mô-đun được nhập lần đầu tiên, Python tìm kiếm mô-đun và nếu tìm thấy, nó sẽ tạo một đối tượng mô-đun 1, khởi tạo nó . Nếu không tìm thấy mô-đun được đặt tên, Lỗi ModuleNotFoundError sẽ xuất hiện. Python thực hiện các chiến lược khác nhau để tìm kiếm mô-đun được đặt tên khi máy móc nhập khẩu được gọi.

Các thư viện Python được lưu trữ ở đâu?

Thông thường, thư viện Python nằm trong thư mục site-packages trong thư mục cài đặt Python , tuy nhiên, nếu nó không nằm trong thư mục .

Đường dẫn nhập trong Python là gì?

Đây là cách dễ nhất để nhập mô-đun Python bằng cách thêm đường dẫn mô-đun vào biến đường dẫn . Biến đường dẫn chứa các thư mục Trình thông dịch Python tìm kiếm các mô-đun đã được nhập trong tệp nguồn. cú pháp. hệ thống. con đường. chắp thêm("module_path")