Mô-đun Python có thể nhập mô-đun khác không?

Bạn có biết nếu bạn làm việc với các đối tượng ban đầu sau khi nhập chúng không?

Con ngựa nào là bản gốc và con ngựa nào là bản sao? . Nguồn. Hình ảnh của tác giả

Việc bạn nên nhập một mô-đun (import module) hay nhập các đối tượng từ một mô-đun (from module import obj1, obj2) không phải là một vấn đề mới. Trong bài viết này, tôi sẽ không phát minh lại bánh xe. Chúng tôi biết rằng việc nhập một mô-đun có nhiều lợi thế hơn, nhưng trong một số trường hợp, các đối tượng nhập trực tiếp từ một mô-đun hoạt động hoàn toàn tốt; . Nếu bạn muốn đọc thêm về chủ đề này, cuốn sách của Mark Lutz (Lutz 2013) cung cấp cách đọc tốt

Có một sự thật về việc nhập rất dễ quên. Một trong hai phương pháp này không nhập các đối tượng từ mô-đun khác mà thay vào đó tạo các bản sao của chúng. Mặc dù thông thường điều này không tạo ra nhiều sự khác biệt, nhưng đôi khi nó có thể xảy ra và sự khác biệt có thể là đáng kể

Trong bài viết này, tôi so sánh việc sao chép các đối tượng từ một mô-đun thay vì nhập chúng. Tôi sẽ chỉ ra một lợi thế của việc nhập một mô-đun so với nhập các đối tượng từ một mô-đun, một lợi thế đôi khi có thể giúp bạn tránh được nhiều rắc rối

Nhập một mô-đun. Các đối tượng ban đầu đang hoạt động

Giả sử chúng ta có một ứng dụng bao gồm ba mô-đun

  • helpers, chứa các biến và hàm được sử dụng trong ứng dụng chính;
  • action, chứa chức năng chính của ứng dụng;
  • __main__, chịu trách nhiệm chạy ứng dụng

Đây là nội dung của helpers

Tôi đã làm cho nó đơn giản nhất có thể. Bây giờ action

Vì vậy, run_app() là chức năng chính của ứng dụng. Nó sẽ được gọi trong mô-đun __main__, chịu trách nhiệm chạy ứng dụng. Tất nhiên, trong ví dụ của chúng ta, __main__ cũng được đơn giản hóa quá mức

Điều này hoạt động như mong đợi. Khi chúng tôi nhập mô-đun helpers, chúng tôi có thể sử dụng hàm from module import obj1, obj21 và biến toàn cục from module import obj1, obj22 của nó. Lưu ý rằng from module import obj1, obj22 không viết hoa vì nó không phải là hằng số; . Ở đây, "toàn cầu" có nghĩa là toàn cầu cho mô-đun; . đây là cách phạm vi hoạt động trong Python (Lutz 2013, Ramalho 2022). Sau đó, chúng tôi thay đổi from module import obj1, obj24 và from module import obj1, obj25, và thay đổi này được phản ánh ở vị trí ban đầu, nghĩa là, trong helpers. Chúng ta có thể thấy điều này bằng cách gọi from module import obj1, obj27, một hàm gọi from module import obj1, obj24 và from module import obj1, obj25

Nhập khẩu từ một mô-đun. Sao chép trong hành động

Bây giờ, chúng ta hãy thay đổi cách chúng ta nhập các đối tượng helpers trong __main__

Đó là một sự khác biệt khá. Chuyện gì đã xảy ra thế?

Chúng tôi đã thay đổi from module import obj1, obj22 và helpers3, nhưng điều này không ảnh hưởng đến các đối tượng ban đầu, nằm ở helpers. Điều này là do helpers5 tạo một bản sao của đối tượng đã nhập. Điều này rất quan trọng, vì vậy hãy để tôi nhấn mạnh

helpers5 tạo một bản sao của đối tượng đã nhập

Do đó, mặc dù những thay đổi của chúng tôi đối với from module import obj1, obj22 và from module import obj1, obj21 ảnh hưởng đến các bản sao này, nhưng chúng không ảnh hưởng đến các giá trị ban đầu từ mô-đun helpers. Hàm from module import obj1, obj27 không sử dụng các bản sao này;

Điều đó có nghĩa là gì?

Điều này có ý nghĩa rất nhiều, và thay đổi rất nhiều. May mắn thay, trong hầu hết các trường hợp, điều này không có vấn đề gì, bởi vì chúng tôi hiếm khi thay đổi các đối tượng từ mô-đun này sang mô-đun khác. Tuy nhiên, đôi khi, chúng tôi làm, và sau đó chúng tôi phải cẩn thận

Để làm cho mọi thứ trở nên phức tạp hơn, chúng ta cần xem xét hai tình huống. nhập các đối tượng không thay đổi và nhập các đối tượng có thể thay đổi từ một mô-đun

đối tượng bất biến

Khi bạn nhập một đối tượng bất biến từ một mô-đun, như trong action2, một bản sao của đối tượng sẽ được tạo. Vì vậy, không gian tên chung của bạn bây giờ sẽ chứa hai đối tượng được gọi là from module import obj1, obj22. from module import obj1, obj24 và action5. Bạn có mong đợi điều này xảy ra?

Theo cách tương tự, chức năng from module import obj1, obj21 không bị thay đổi ở vị trí ban đầu. Có hai hàm from module import obj1, obj21 sau khi nhập. action8 và action9

Do đó, khi chúng tôi nhập một mô-đun (__main__0), chúng tôi sử dụng các đối tượng ban đầu (bất biến) trực tiếp từ mô-đun;

Tuy nhiên, khi chúng tôi nhập các đối tượng bất biến trực tiếp từ một mô-đun (__main__1), Python tạo các bản sao của chúng và chúng tôi sử dụng các bản sao này — không phải các đối tượng ban đầu. Do đó, khi các đối tượng ban đầu thay đổi trong mô-đun, điều này sẽ không ảnh hưởng đến các đối tượng chúng tôi đang sử dụng — bởi vì chúng tôi đang sử dụng các bản sao của chúng

đối tượng có thể thay đổi

Các đối tượng có thể thay đổi không thể được sao chép và bất kỳ thay đổi nào đối với các đối tượng ban đầu của chúng hoặc cái gọi là bản sao đều được thực hiện đối với các đối tượng ban đầu. Điều này là do Python không tạo bản sao của đối tượng có thể thay đổi;

Tính năng này của các đối tượng có thể thay đổi ảnh hưởng đến cách nhập hoạt động

Hãy đơn giản hóa ứng dụng của chúng tôi. Bây giờ nó sẽ chỉ chứa một đối tượng, một từ điển, là một đối tượng Python có thể thay đổi. Vì vậy, helpers như sau

Mô-đun action chứa hàm run_app(), hiện chỉ trả về từ điển from module import obj1, obj22

Và đây là mô-đun __main__

Bạn có thấy những gì đã xảy ra? . đối tượng ban đầu đã được thay đổi tại chỗ

Hành vi này là điển hình cho các đối tượng có thể thay đổi. Python không tạo bản sao của chúng; . Vì vậy, việc thay đổi giá trị của đối tượng được gán cho bất kỳ tên nào trong số này sẽ có tác dụng tương tự. đối tượng ban đầu sẽ bị ảnh hưởng

Kết hợp nhập khẩu

Kết hợp hai loại nhập sẽ không thay đổi gì. Các đối tượng không thể thay đổi sẽ hoạt động giống như các đối tượng không thể thay đổi và các đối tượng có thể thay đổi sẽ hoạt động giống như các đối tượng có thể thay đổi. Hãy xem nó hoạt động như thế nào. Bạn có thể coi đó là một bài tập

Lần này, chúng tôi sẽ chỉ làm việc với hai mô-đun, helpers__main__. Cái trước sẽ xác định hai đối tượng, một bất biến và một có thể thay đổi

Trong __main__, chúng tôi sẽ nhập chúng theo cả hai cách và sau đó thay đổi các đối tượng. Trước khi tiếp tục, hãy thử đoán đầu ra của đoạn mã dưới đây. Tôi đã thêm dấu ngoặc kép vào những chỗ mà bạn nên đoán kết quả, sau mỗi helpers2

Và đây là đầu ra

Tôi hy vọng bạn hiểu đúng. Hiểu khía cạnh này của việc nhập các đối tượng và mô-đun sẽ giúp bạn tránh được các lỗi lạ có thể xảy ra khi ứng dụng của bạn chứa một số mô-đun với một số phân cấp phụ thuộc (e. g. , helpers3 nhập khẩu helpers4 nhập khẩu helpers5)

Bản tóm tắt

Như tôi đã đề cập ở trên, việc hiểu những điều phức tạp này của nhập Python sẽ giúp bạn tránh được những sai lầm trong các ứng dụng bao gồm một số mô-đun. Nhớ

  • import module cho phép bạn sử dụng các đối tượng từ mô-đun này như helpers7. Điều này có nghĩa là bạn sử dụng đối tượng ban đầu, đối tượng nằm ở helpers8. Do đó, bất kỳ thay đổi nào đối với chính đối tượng này, được thực hiện ở bất kỳ đâu trong mã như helpers9, sẽ được phản ánh trong lệnh gọi của bạn tới helpers7
  • Trong trường hợp các đối tượng không thể thay đổi, action1 tạo một bản sao của đối tượng. Điều này có nghĩa là bản sao này nằm trong phạm vi của mô-đun (trong mô-đun mà bạn đã nhập action2). Thay đổi giá trị của bản sao này (i. e. , action2) sẽ không ảnh hưởng đến đối tượng ban đầu (helpers7)
  • Trong trường hợp các đối tượng có thể thay đổi, action1 chỉ tạo một tên mới của đối tượng. Do đó, việc thay đổi giá trị của nó (i. e. , action6) sẽ ảnh hưởng đến đối tượng ban đầu (helpers7)
  • Do đó, nhập mô-đun (import module) thay vì nhập các đối tượng từ nó (action1) sẽ an toàn hơn. Điều này là do bạn luôn làm việc với các đối tượng nằm trực tiếp trong mô-đun và các bản sao của chúng không bao giờ được tạo. Do đó, nó đủ để theo dõi những gì đang xảy ra với các đối tượng ban đầu (đối tượng nằm trong helpers8) và bất kỳ mô-đun nào khác làm gì với đối tượng này, chúng sẽ làm điều đó với đối tượng ban đầu. Khi bạn cần tạo một bản sao, bạn có thể làm điều đó một cách rõ ràng, nơi bạn cần.

Điểm cuối cùng có lẽ là quan trọng nhất. Khi bạn cần một bản sao, hãy tạo nó, nhưng hãy làm điều đó một cách rõ ràng khi bạn cần bản sao này. Tạo bản sao theo cách nhập cụ thể chắc chắn là ẩn ý, ​​gián tiếp và không rõ ràng. Người mới bắt đầu và thậm chí một số Pythonistas trung cấp có thể quên hoặc hoàn toàn không biết rằng action1 tạo một bản sao hoặc tên mới của helpers7 và phạm vi của mô-đun __main__ hiện chứa hai đối tượng

  • trong trường hợp đối tượng bất biến, đối tượng helpers7 và action2;
  • trong trường hợp các đối tượng có thể thay đổi, nó thực sự là một đối tượng có hai tên. helpers7 và run_app()7

Tất nhiên, tất cả những điều này đều quan trọng khi bạn thay đổi đối tượng từ mô-đun này sang mô-đun khác. Vậy thì bạn phải cẩn thận, và đây là lúc tất cả những điều chúng ta đang thảo luận có thể xảy ra.

Tôi nghĩ tốt hơn hết là tránh nhầm lẫn khi tạo các bản sao của các đối tượng bằng cách nhập. Đây là lý do tại sao, ít nhất là trong ngữ cảnh này, việc nhập một mô-đun sẽ an toàn hơn là nhập các đối tượng từ nó

Hai mô-đun có thể nhập lẫn nhau không?

Như đã giải thích trong câu trả lời của tôi, các mô-đun có thể nhập lẫn nhau , nhưng nếu bạn cần làm điều đó, bạn có thể muốn .

Một mô-đun có thể tự nhập Python không?

Không cần mô-đun tự nhập . Một mô-đun tự nhập có thể dẫn đến lỗi vì mô-đun có thể ở trạng thái không hoàn chỉnh khi tự nhập.

Bạn có thể nhập hai mô-đun bằng Python không?

Nhập nhiều mô-đun . Nhập khẩu thường phải trên các dòng riêng biệt. Nếu bạn sử dụng from để nhập hàm, biến, lớp, v.v. , như được giải thích tiếp theo, bạn có thể tách chúng bằng dấu phẩy. You can write multiple modules separated by commas after the import statement, but this is not recommended in PEP8. Imports should usually be on separate lines. If you use from to import functions, variables, classes, etc., as explained next, you can separate them with a comma.

2 tập lệnh Python có thể nhập lẫn nhau không?

Bạn có thể có mã mà bạn muốn tự chạy và nhập từ các tập lệnh khác. Trong trường hợp đó, thường đáng để 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ù bạn nên tách biệt các tập lệnh và thư viện, nhưng tất cả các tệp Python đều có thể được thực thi và nhập .