Giống như các bài viết? . Python đơn giản đã chết của Jason C. McDonald có sẵn từ No Starch Press Phần tồi tệ nhất của hướng dẫn luôn là sự đơn giản của chúng, phải không? Tôi nhận thấy rằng việc cấu trúc một dự án Python là một trong những thành phần thường bị bỏ qua nhất khi dạy ngôn ngữ này. Tệ hơn nữa, nhiều nhà phát triển đã mắc sai lầm, vấp phải một mớ lỗi phổ biến cho đến khi họ tìm ra thứ ít nhất hoạt động được. Đây là tin tốt. bạn không cần phải là một trong số họ Trong phần này của loạt bài Dead Simple Python, chúng ta sẽ khám phá 5 câu lệnh, mô-đun, gói và cách khớp mọi thứ lại với nhau mà không làm bạn rối tóc. Chúng tôi thậm chí sẽ đề cập đến VCS, PEP và Zen of Python. Thắt dây an toànThiết lập kho lưu trữTrước khi chúng tôi đi sâu vào cấu trúc dự án thực tế, hãy giải quyết cách nó phù hợp với Hệ thống kiểm soát phiên bản [VCS] của chúng tôi. bắt đầu với thực tế là bạn cần một VCS. Một vài lý do là
Bạn đã có rất nhiều lựa chọn có sẵn cho bạn. Git là rõ ràng nhất, đặc biệt nếu bạn không biết sử dụng cái gì khác. Bạn có thể lưu trữ miễn phí kho lưu trữ Git của mình trên GitHub, GitLab, Bitbucket hoặc Gitote, trong số những thứ khác. Nếu bạn muốn thứ gì khác ngoài Git, thì có hàng tá tùy chọn khác, bao gồm Mercurial, Bazaar, Subversion (mặc dù nếu bạn sử dụng cái cuối cùng đó, có thể bạn sẽ bị đồng nghiệp coi là khủng long. ) Tôi sẽ lặng lẽ cho rằng bạn đang sử dụng Git cho phần còn lại của hướng dẫn này, vì đó là những gì tôi chỉ sử dụng Khi bạn đã tạo kho lưu trữ của mình và sao chép một bản sao cục bộ vào máy tính của mình, bạn có thể bắt đầu thiết lập dự án của mình. Tối thiểu, bạn sẽ cần tạo những thứ sau
Đúng rồi. các tệp mã Python của chúng tôi thực sự thuộc về một thư mục con riêng biệt. Điều này rất quan trọng, vì thư mục gốc của kho lưu trữ của chúng ta sẽ trở nên lộn xộn với các tệp xây dựng, tập lệnh đóng gói, môi trường ảo và tất cả những thứ khác không thực sự là một phần của mã nguồn Ví dụ, chúng tôi sẽ gọi dự án hư cấu của chúng tôi là 9PEP 8 và đặt tênPhong cách Python được điều chỉnh phần lớn bởi một bộ tài liệu có tên là Đề xuất cải tiến Python, viết tắt là PEP. Tất nhiên, không phải tất cả các PEP đều được thông qua - đó là lý do tại sao chúng được gọi là "Đề xuất" - nhưng một số. Bạn có thể duyệt qua chỉ mục PEP chính trên trang web chính thức của Python. Chỉ số này chính thức được gọi là PEP 0 Ngay bây giờ, chúng tôi chủ yếu quan tâm đến PEP 8, lần đầu tiên được tác giả bởi người tạo ngôn ngữ Python Guido van Rossum vào năm 2001. Đây là tài liệu phác thảo chính thức phong cách mã hóa mà tất cả các nhà phát triển Python thường nên tuân theo. Giữ nó dưới gối của bạn. Học nó, làm theo nó, khuyến khích người khác làm như vậy (Ghi chú bên lề. PEP 8 đưa ra quan điểm rằng luôn có ngoại lệ đối với quy tắc kiểu. Đó là một hướng dẫn, không phải là một nhiệm vụ. ) Ngay bây giờ, chúng tôi chủ yếu quan tâm đến phần có tiêu đề
Chúng ta sẽ tìm hiểu chính xác các mô-đun và gói là gì trong giây lát, nhưng hiện tại, hãy hiểu rằng các mô-đun được đặt tên theo tên tệp và các gói được đặt tên theo tên thư mục của chúng Nói cách khác, tên tệp phải là chữ thường, có dấu gạch dưới nếu điều đó cải thiện khả năng đọc. Tương tự, tên thư mục phải là chữ thường, không có dấu gạch dưới nếu có thể tránh được. Nói cách khác
Tôi biết, tôi biết, cách dài dòng để đưa ra quan điểm, nhưng ít nhất tôi đã đặt một chút PEP vào bước của bạn. (Xin chào? Cái này có bật không?) Gói và Mô-đunĐiều này sẽ khiến bạn cảm thấy khó chịu, nhưng đây là những định nghĩa đã được hứa hẹn Bất kỳ tệp Python (_______52) nào cũng là một mô-đun và một loạt các mô-đun trong một thư mục là một gói Tốt. gần như. Có một việc khác mà bạn phải làm với một thư mục để biến nó thành một gói, đó là dán một tệp có tên 3 vào đó. Bạn thực sự không cần phải đặt bất cứ thứ gì vào tập tin đó. Nó chỉ cần ở đóCó những thứ thú vị khác mà bạn có thể làm với 3, nhưng nó nằm ngoài phạm vi của hướng dẫn này, vì vậyNếu bạn quên 3 trong gói của mình, thì nó sẽ làm điều gì đó kỳ lạ hơn là chỉ thất bại, bởi vì điều đó làm cho nó trở thành một gói không gian tên ẩn. Có một số điều tiện lợi mà bạn có thể làm với loại gói đặc biệt đó, nhưng tôi sẽ không đi sâu vào vấn đề đó ở đây. Như thường lệ, bạn có thể tìm hiểu thêm bằng cách đọc tài liệu. PEP 420. Gói không gian tên tiềm ẩnVì vậy, nếu chúng ta nhìn vào cấu trúc dự án của mình, thì 9 thực sự là một gói và nó có thể chứa các gói khác. Do đó, chúng tôi có thể gọi ____09 gói cấp cao nhất của chúng tôi và tất cả các gói bên dưới các gói con của nó. Điều này sẽ thực sự quan trọng khi chúng ta bắt đầu nhập nội dungHãy xem một ảnh chụp nhanh các dự án trong thế giới thực của tôi, 8, để biết cách chúng tôi cấu trúc các thứ 4Vào chế độ toàn màn hình Thoát chế độ toàn màn hình (Trong trường hợp bạn đang thắc mắc, tôi đã sử dụng chương trình UNIX 9 để tạo sơ đồ nhỏ ở trên. )Bạn sẽ thấy rằng tôi có một gói cấp cao nhất có tên là 8, với bốn gói phụ. 71, 72, 73 và 74. Tôi cũng có thư mục 75, nhưng thư mục đó chỉ chứa âm thanh, hình ảnh của trò chơi, v.v. (bỏ qua ở đây cho ngắn gọn). 75 KHÔNG phải là một gói vì nó không chứa một 3Tôi cũng có một tệp đặc biệt khác trong gói cấp cao nhất của mình. 78. Đây là tệp được chạy khi chúng tôi thực thi gói cấp cao nhất của mình trực tiếp qua 79. Lát nữa chúng ta sẽ nói về những gì diễn ra trong 78 đóCách nhập hoạt độngNếu bạn đã từng viết bất kỳ mã Python có ý nghĩa nào trước đây, thì gần như chắc chắn bạn đã quen thuộc với câu lệnh 5. Ví dụ
Vào chế độ toàn màn hình Thoát chế độ toàn màn hình Thật hữu ích khi biết rằng, khi chúng tôi nhập một mô-đun, chúng tôi thực sự đang chạy nó. Điều này có nghĩa là bất kỳ câu lệnh 5 nào trong mô-đun cũng đang được chạyVí dụ: có một số câu lệnh nhập của riêng nó, được thực thi khi chúng ta nói 04. Điều đó không có nghĩa là chúng có sẵn cho tệp mà chúng tôi đã nhập 05 từ đó, nhưng điều đó có nghĩa là những tệp đó phải tồn tại. Nếu (vì một số lý do không chắc chắn) 06 đã bị xóa trên môi trường của bạn và bạn đã chạy 04, nó sẽ không thành công với lỗi
Đương nhiên, đọc đến đây, bạn có thể hơi bối rối. Tôi đã có người hỏi tôi tại sao không thể tìm thấy mô-đun bên ngoài (trong ví dụ này là 05). Những người khác thắc mắc tại sao mô-đun bên trong ( 09 ở đây) lại được nhập vào, vì họ không yêu cầu trực tiếp trong mã của mình. Đáp án đơn giản. chúng tôi đã nhập 05 và nhập khẩu đó 09Tất nhiên, kịch bản trên là hư cấu. 82 và 04 sẽ không bao giờ bị lỗi trong các trường hợp bình thường, bởi vì cả hai mô-đun đều là một phần của thư viện lõi của Python. Đó chỉ là một ví dụ ngớ ngẩn. ;)Nhập khẩu Nên và Không nênThực tế có một số cách nhập, nhưng hầu hết chúng hiếm khi được sử dụng. Đối với tất cả các ví dụ bên dưới, chúng tôi sẽ tưởng tượng rằng chúng tôi có một tệp có tên là 84
Vào chế độ toàn màn hình Thoát chế độ toàn màn hình Ví dụ: chúng tôi sẽ chạy phần còn lại của mã trong phần này trong trình bao tương tác Python, từ cùng thư mục với 84Nếu chúng tôi muốn chạy chức năng 86, trước tiên chúng tôi phải nhập mô-đun 87. Cách dễ nhất để làm điều này là 7Vào chế độ toàn màn hình Thoát chế độ toàn màn hình Chúng tôi thực sự sẽ nói rằng 87 là không gian tên của 86 và 80. Các nhà phát triển Python thực sự thích các không gian tên, bởi vì chúng làm cho nó rõ ràng các chức năng đến từ đâu và không có gì.(Nhân tiện, đừng nhầm lẫn không gian tên với gói không gian tên ẩn. Chúng là hai thứ khác nhau. ) Zen of Python, còn được gọi là PEP 20, xác định triết lý đằng sau ngôn ngữ Python. Dòng cuối cùng có một tuyên bố giải quyết vấn đề này
Tuy nhiên, tại một thời điểm nhất định, các không gian tên có thể trở thành một vấn đề khó khăn, đặc biệt là với các gói lồng nhau. 81 thật xấu xí. Rất may, chúng tôi có một cách xung quanh việc phải sử dụng không gian tên mỗi khi chúng tôi gọi hàmNếu chúng ta muốn có thể sử dụng hàm 86 mà không cần phải liên tục đặt trước tên mô-đun của nó, chúng ta có thể làm điều này thay thế 0Vào chế độ toàn màn hình Thoát chế độ toàn màn hình Tuy nhiên, xin lưu ý rằng cả 80 và 84 đều không hoạt động trong trường hợp cuối cùng đó, bởi vì chúng tôi đã không nhập hàm hoàn toàn. Để sử dụng nó, chúng tôi phải thay đổi mã này 8Vào chế độ toàn màn hình Thoát chế độ toàn màn hình Trong cơn ác mộng gói lồng nhau khủng khiếp trước đó, bây giờ chúng ta có thể nói 85, và sau đó chỉ cần sử dụng trực tiếp 86. Ngoài ra, nếu chúng tôi muốn một chút không gian tên, chúng tôi có thể nói 87 và nói 88Hệ thống 5 ngon dẻo như thếTuy nhiên, chẳng bao lâu sau, bạn có thể sẽ thấy mình nói "Nhưng tôi có hàng trăm chức năng trong mô-đun của mình và tôi muốn sử dụng tất cả chúng. " Đây là thời điểm mà nhiều nhà phát triển đi chệch hướng khi làm điều này 8Vào chế độ toàn màn hình Thoát chế độ toàn màn hình Điều này rất, rất tệ. Nói một cách đơn giản, nó nhập trực tiếp mọi thứ trong mô-đun và đó là một vấn đề. Hãy tưởng tượng đoạn mã sau 1Vào chế độ toàn màn hình Thoát chế độ toàn màn hình Bạn nghĩ điều gì sẽ xảy ra? . 12 đã bị che khuất - chúng tôi không thể gọi nó là 86, điều đó có nghĩa là chúng tôi hoàn toàn không thể gọi nóTất nhiên, vì chúng ta thường không biết, hoặc ít nhất là không nhớ, từng chức năng, lớp và biến trong mọi mô-đun được nhập, nên chúng ta có thể dễ dàng kết thúc với rất nhiều mớ hỗn độn. Zen of Python cũng giải quyết tình huống này
Bạn không bao giờ phải đoán xem hàm hoặc biến đến từ đâu. Một nơi nào đó trong tệp phải là mã cho chúng tôi biết rõ ràng nó đến từ đâu. Hai kịch bản đầu tiên chứng minh rằng Tôi cũng nên đề cập rằng kịch bản 81 trước đó là điều mà các nhà phát triển Python KHÔNG muốn thấy. Cũng từ Thiền của Python
Một số gói lồng vào nhau thì không sao, nhưng khi dự án của bạn bắt đầu trông giống như một bộ búp bê Matryoshka phức tạp, thì bạn đã làm sai điều gì đó. Sắp xếp các mô-đun của bạn thành các gói, nhưng giữ cho nó đơn giản hợp lý Nhập trong dự án của bạnCấu trúc tệp dự án mà chúng tôi đã tạo trước đó sắp trở nên rất hữu ích. Nhớ lại dự án 8 của tôi 8Vào chế độ toàn màn hình Thoát chế độ toàn màn hình Trong mô-đun 16 của tôi, được xác định bởi 17, tôi muốn sử dụng lớp 18 của mình. Lớp đó được định nghĩa trong 19. Làm thế nào để tôi có được nó?Bởi vì tôi đã định nghĩa 8 là một gói và sắp xếp các mô-đun của mình thành các gói con, nên nó thực sự khá dễ dàng. Trong 81, tôi nói 5Vào chế độ toàn màn hình Thoát chế độ toàn màn hình Điều này được gọi là nhập khẩu tuyệt đối. Nó bắt đầu ở gói cấp cao nhất, 8, và đi xuống gói 71, nơi nó tìm kiếm 84Một số nhà phát triển đến gặp tôi với các câu lệnh nhập giống như 85 hơn và tự hỏi tại sao nó không hoạt động. Nói một cách đơn giản, gói 72 (nơi 81 sống) không biết gì về các gói anh chị em của nóNó, tuy nhiên, biết về cha mẹ của nó. Do đó, Python có một thứ gọi là nhập tương đối cho phép chúng ta làm điều tương tự như thế này thay vì 0Vào chế độ toàn màn hình Thoát chế độ toàn màn hình 88 có nghĩa là "gói mẹ trực tiếp của gói này", trong trường hợp này là 8. Vì vậy, quá trình nhập lùi lại một cấp, đi xuống 71 và tìm thấy 84Có rất nhiều tranh luận về việc nên sử dụng nhập khẩu tuyệt đối hay tương đối. Cá nhân tôi thích sử dụng nhập khẩu tuyệt đối bất cứ khi nào có thể, bởi vì nó làm cho mã dễ đọc hơn rất nhiều. Bạn có thể tự quyết định, tuy nhiên. Phần quan trọng duy nhất là kết quả hiển nhiên - không nên có bí ẩn về nguồn gốc của bất kỳ thứ gì (Đọc Tiếp. Real Python - Nhập khẩu tuyệt đối và tương đối trong Python Có một gotcha ẩn nấp khác ở đây. Trong 52, tôi có dòng này 1Vào chế độ toàn màn hình Thoát chế độ toàn màn hình Chắc chắn, vì cả hai mô-đun này nằm trong cùng một gói, chúng ta có thể chỉ cần nói 53, phải không?Sai. Nó thực sự sẽ không xác định được vị trí của 81. Điều này là do chúng tôi đang chạy gói cấp cao nhất 8, có nghĩa là đường dẫn tìm kiếm (nơi Python tìm kiếm các mô-đun và theo thứ tự nào) hoạt động khácTuy nhiên, chúng ta có thể sử dụng nhập tương đối để thay thế 2Vào chế độ toàn màn hình Thoát chế độ toàn màn hình Trong trường hợp đó, 56 duy nhất có nghĩa là "gói này"Nếu bạn đã quen thuộc với hệ thống tệp UNIX điển hình, điều này sẽ bắt đầu có ý nghĩa. 88 có nghĩa là "lùi một cấp" và 56 có nghĩa là "vị trí hiện tại". Tất nhiên, Python tiến thêm một bước. 59 có nghĩa là "lùi hai cấp", 00 là "lùi ba cấp", v.v.Tuy nhiên, hãy nhớ rằng những "cấp độ" đó không chỉ là những thư mục đơn giản, ở đây. Chúng là những gói. Nếu bạn có hai gói riêng biệt trong một thư mục đơn giản KHÔNG phải là gói, bạn không thể sử dụng nhập tương đối để chuyển từ gói này sang gói khác. Bạn sẽ phải làm việc với đường dẫn tìm kiếm Python cho điều đó và điều đó nằm ngoài phạm vi của hướng dẫn này. (Xem tài liệu ở cuối bài viết này. ) 78Bạn có nhớ khi tôi đề cập đến việc tạo một 78 trong gói cấp cao nhất của chúng tôi không? . Gói 8 của tôi có thể được chạy từ thư mục gốc của kho lưu trữ của tôi với 79Đây là nội dung của tập tin đó 3Vào chế độ toàn màn hình Thoát chế độ toàn màn hình Vâng, đó thực sự là nó. Tôi đang nhập mô-đun của mình 05 từ gói cấp cao nhất 8Hãy nhớ rằng, tôi cũng có thể nói 07 thay vì. Ngoài ra, nếu tôi chỉ muốn nói 08 thay vì 09, tôi có thể đã làm 10 hoặc 11. Cuối cùng, nó không tạo ra nhiều khác biệt về mặt kỹ thuật CÁCH tôi thực hiện thao tác nhập đó, miễn là mã có thể đọc được(Ghi chú bên lề. Chúng ta có thể tranh luận xem việc tôi có một 12 riêng cho chức năng chính của mình là 08 có hợp lý hay không, nhưng tôi có lý do của mình. và chúng nằm ngoài phạm vi của hướng dẫn này. )Phần khiến hầu hết mọi người bối rối lúc đầu là toàn bộ câu lệnh 14. Python không có nhiều bản soạn sẵn - mã phải được sử dụng khá phổ biến mà ít hoặc không cần sửa đổi - nhưng đây là một trong những bit hiếm hoi đó 15 là thuộc tính chuỗi đặc biệt của mọi mô-đun Python. Nếu tôi dán dòng 16 ở đầu 52, khi mô-đun đó được nhập (và do đó chạy), chúng ta sẽ thấy "thiếu sót. dữ liệu. cài đặt" được in raKhi một mô-đun được chạy trực tiếp qua 18, mô-đun đó được gán một giá trị đặc biệt là 15. "chủ yếu"Do đó, 20 thực sự đang kiểm tra xem mô-đun có đang được thực thi với tư cách là mô-đun chính hay không. Nếu có, nó sẽ chạy mã theo điều kiệnBạn có thể thấy điều này trong hành động theo một cách khác. Nếu tôi thêm phần sau vào dưới cùng của 12 4Vào chế độ toàn màn hình Thoát chế độ toàn màn hình Sau đó, tôi có thể thực thi mô-đun đó trực tiếp thông qua 22 và kết quả giống như 79. Bây giờ 78 đang bị bỏ qua hoàn toàn và 15 của 26 là 27Trong khi đó, nếu tôi chỉ chạy 79, mã đặc biệt đó trong 12 sẽ bị bỏ qua, vì 15 của nó bây giờ lại là 31Xem nó hoạt động như thế nào? kết thúcHãy xem xét
Tất nhiên, có rất nhiều khái niệm và thủ thuật nâng cao hơn mà chúng ta có thể sử dụng để cấu trúc một dự án Python, nhưng chúng ta sẽ không thảo luận về điều đó ở đây. Tôi thực sự khuyên bạn nên đọc các tài liệu
Cảm ơn bạn tới 37, 38 (Freenode IRC 39), @cbrintnall và @rhymes (Dev) về những sửa đổi được đề xuất |