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 độngGiả 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, obj2
1 và biến toàn cục from module import obj1, obj2
2 của nó. Lưu ý rằng from module import obj1, obj2
2 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, obj2
4 và from module import obj1, obj2
5, 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, obj2
7, một hàm gọi from module import obj1, obj2
4 và from module import obj1, obj2
5
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, obj2
2 và helpers
3, 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 helpers
5 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
helpers
5 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, obj2
2 và from module import obj1, obj2
1 ả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, obj2
7 không sử dụng các bản sao này;
Đ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 action
2, 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, obj2
2. from module import obj1, obj2
4 và action
5. 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, obj2
1 không bị thay đổi ở vị trí ban đầu. Có hai hàm from module import obj1, obj2
1 sau khi nhập. action
8 và action
9
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, obj2
2
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 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
và __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 helpers
2
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. , helpers
3 nhập khẩu helpers
4 nhập khẩu helpers
5]
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ưhelpers
7. Điều này có nghĩa là bạn sử dụng đối tượng ban đầu, đối tượng nằm ởhelpers
8. 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ưhelpers
9, sẽ được phản ánh trong lệnh gọi của bạn tớihelpers
7- Trong trường hợp các đối tượng không thể thay đổi,
action
1 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ậpaction
2]. Thay đổi giá trị của bản sao này [i. e. ,action
2] sẽ không ảnh hưởng đến đối tượng ban đầu [helpers
7] - Trong trường hợp các đối tượng có thể thay đổi,
action
1 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. ,action
6] sẽ ảnh hưởng đến đối tượng ban đầu [helpers
7] - Do đó, nhập mô-đun [
import module
] thay vì nhập các đối tượng từ nó [action
1] 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 tronghelpers
8] 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 action
1 tạo một bản sao hoặc tên mới của helpers
7 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
helpers
7 vàaction
2; - 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.
helpers
7 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ó