Cấu trúc dự án tốt nhất cho ứng dụng Python là gì? [đóng cửa]


730

Hãy tưởng tượng rằng bạn muốn phát triển một ứng dụng máy tính để bàn (không phải web) cho người dùng cuối không tầm thường trong Python. Cách tốt nhất để cấu trúc phân cấp thư mục của dự án là gì?

Các tính năng mong muốn là dễ bảo trì, thân thiện với IDE, sự phù hợp để phân nhánh / hợp nhất kiểm soát nguồn và tạo các gói cài đặt dễ dàng.

Đặc biệt:

  1. Bạn đặt nguồn ở đâu?
  2. Nơi nào bạn đặt các kịch bản khởi động ứng dụng?
  3. Nơi nào bạn đặt dự án IDE cruft?
  4. Nơi nào bạn đặt các bài kiểm tra đơn vị / chấp nhận?
  5. Nơi nào bạn đặt dữ liệu không phải Python như tệp cấu hình?
  6. Nơi nào bạn đặt các nguồn không phải Python như C ++ cho các mô đun mở rộng nhị phân pyd / so?

Câu trả lời:


378

Không quá nhiều vấn đề. Bất cứ điều gì làm cho bạn hạnh phúc sẽ làm việc. Không có nhiều quy tắc ngớ ngẩn vì các dự án Python có thể đơn giản.

  • /scriptshoặc /bincho loại công cụ giao diện dòng lệnh đó
  • /tests cho bài kiểm tra của bạn
  • /lib cho các thư viện ngôn ngữ C của bạn
  • /doc cho hầu hết các tài liệu
  • /apidoc cho các tài liệu API do Epydoc tạo.

Và thư mục cấp cao nhất có thể chứa README, Config và whatnot.

Sự lựa chọn khó khăn là có hay không sử dụng /srccây. Python không có một sự phân biệt giữa /src, /lib/binnhư Java hay C có.

Vì một /srcthư mục cấp cao nhất được một số người coi là vô nghĩa, thư mục cấp cao nhất của bạn có thể là kiến ​​trúc cấp cao nhất của ứng dụng của bạn.

  • /foo
  • /bar
  • /baz

Tôi khuyên bạn nên đặt tất cả những thứ này trong thư mục "tên sản phẩm của tôi". Vì vậy, nếu bạn đang viết một ứng dụng có tên quux, thư mục chứa tất cả những thứ này được đặt tên /quux.

PYTHONPATHSau đó, một dự án khác có thể bao gồm /path/to/quux/foođể sử dụng lại QUUX.foomô-đun.

Trong trường hợp của tôi, vì tôi sử dụng Komodo Edit, cuft IDE của tôi là một tệp .KPF duy nhất. Tôi thực sự đã đặt nó trong /quuxthư mục cấp cao nhất và bỏ qua việc thêm nó vào SVN.


23
Bất kỳ dự án python mã nguồn mở nào bạn muốn giới thiệu mô phỏng cấu trúc thư mục của họ?
Lance vội vã

4
Nhìn vào Django cho một ví dụ tốt.
S.Lott

33
Tôi không có xu hướng coi Django là một ví dụ điển hình - chơi các thủ thuật với sys.path là một DQ tức thì trong cuốn sách của tôi.
Charles Duffy

18
lại "thủ thuật": Django thêm cha mẹ của thư mục dự án gốc vào sys.path, để các mô-đun có thể được nhập dưới dạng "từ klass import.app.module" hoặc "từ klass nhập app.module".
Jonathan Hartley

3
Ồ, tôi thích thủ thuật này và hiện đang sử dụng nó. Tôi muốn đặt mô-đun chia sẻ vào một thư mục khác và tôi không muốn cài đặt mô-đun toàn hệ thống, tôi cũng không muốn yêu cầu mọi người sửa đổi PYTHONPATH theo cách thủ công. Trừ khi mọi người đề xuất một cái gì đó tốt hơn, tôi nghĩ rằng đây thực sự là cách tốt nhất để đi.
Yongwei Wu

242

Theo cấu trúc hệ thống tập tin của Jean-Paul Calderone trong một dự án Python :

Project/
|-- bin/
|   |-- project
|
|-- project/
|   |-- test/
|   |   |-- __init__.py
|   |   |-- test_main.py
|   |   
|   |-- __init__.py
|   |-- main.py
|
|-- setup.py
|-- README

23
Project/project/? Ah, tên thứ hai là tên gói.
Cees Timmerman

44
Làm thế nào để tập tin thực thi trong thư mục bin tham chiếu mô-đun dự án? (Tôi không nghĩ cú pháp python cho phép ../trong một tuyên bố bao gồm)
ThorSummoner

8
@ThorSummoner Đơn giản. Bạn cài đặt gói! ( pip install -e /path/to/Project)
Kroltan

22
Thật tuyệt vời nếu ai đó sẽ nén một mẫu của bố cục này với hello.py và hello-test.py và làm cho nó có sẵn cho chúng tôi newbs.
jeremyjjbrown 15/1/2015

8
@Bloke Lõi là -ecờ, cài đặt gói dưới dạng gói có thể chỉnh sửa, nghĩa là cài đặt nó dưới dạng liên kết đến thư mục dự án thực tế. Việc thực thi có thể chỉ cần import projectcó quyền truy cập vào mô-đun.
Kroltan 14/03/2016

232

Bài đăng trên blog này của Jean-Paul Calderone thường được đưa ra như một câu trả lời trong #python trên Freenode.

Cấu trúc hệ thống tệp của dự án Python

Làm:

  • Đặt tên cho thư mục một cái gì đó liên quan đến dự án của bạn. Ví dụ: nếu dự án của bạn được đặt tên là "Twisted", hãy đặt tên thư mục cấp cao nhất cho các tệp nguồn của nó Twisted. Khi bạn phát hành, bạn nên bao gồm hậu tố số phiên bản : Twisted-2.5.
  • tạo một thư mục Twisted/binvà đặt các tệp thực thi của bạn ở đó, nếu bạn có bất kỳ. Đừng cung cấp cho họ một .pyphần mở rộng, ngay cả khi chúng là các tệp nguồn Python. Đừng đặt bất kỳ mã nào vào chúng ngoại trừ việc nhập và gọi đến một hàm chính được xác định ở một nơi khác trong các dự án của bạn. (Hơi nhăn: vì trên Windows, trình thông dịch được chọn bởi phần mở rộng tệp, người dùng Windows của bạn thực sự muốn có phần mở rộng .py. Vì vậy, khi bạn đóng gói cho Windows, bạn có thể muốn thêm nó. Tôi biết để tự động hóa quá trình này. Xem xét rằng trên POSIX, phần mở rộng .py chỉ là một mụn cóc, trong khi trên Windows, thiếu là một lỗi thực tế, nếu cơ sở người dùng của bạn bao gồm người dùng Windows, bạn có thể muốn chọn chỉ có .py mở rộng ở mọi nơi.)
  • Nếu dự án của bạn có thể biểu thị dưới dạng một tệp nguồn Python duy nhất, thì hãy đặt nó vào thư mục và đặt tên cho nó là một cái gì đó liên quan đến dự án của bạn. Ví dụ , Twisted/twisted.py. Nếu bạn cần nhiều tệp nguồn, hãy tạo một gói thay thế ( Twisted/twisted/, với một khoảng trống Twisted/twisted/__init__.py) và đặt các tệp nguồn của bạn vào đó. Ví dụ , Twisted/twisted/internet.py.
  • đặt các bài kiểm tra đơn vị của bạn trong gói phụ của gói của bạn (lưu ý - điều này có nghĩa là tùy chọn tệp nguồn Python duy nhất ở trên là một mẹo - bạn luôn cần ít nhất một tệp khác cho các bài kiểm tra đơn vị của mình). Ví dụ , Twisted/twisted/test/. Tất nhiên, làm cho nó một gói với Twisted/twisted/test/__init__.py. Đặt thử nghiệm trong các tập tin như Twisted/twisted/test/test_internet.py.
  • thêm Twisted/READMETwisted/setup.pyđể giải thích và cài đặt phần mềm của bạn, tương ứng, nếu bạn cảm thấy tốt.

Đừng:

  • đặt nguồn của bạn trong một thư mục được gọi là srchoặc lib. Điều này làm cho nó khó chạy mà không cần cài đặt.
  • đặt các bài kiểm tra của bạn bên ngoài gói Python của bạn. Điều này khiến cho việc chạy thử nghiệm đối với phiên bản đã cài đặt trở nên khó khăn.
  • tạo một gói chỉ có một __init__.pyvà sau đó đặt tất cả mã của bạn vào __init__.py. Chỉ cần tạo một mô-đun thay vì một gói, nó đơn giản hơn.
  • cố gắng đưa ra các bản hack ma thuật để làm cho Python có thể nhập mô-đun hoặc gói của bạn mà không cần người dùng thêm thư mục chứa nó vào đường dẫn nhập của chúng (thông qua PYTHONPATH hoặc một số cơ chế khác). Bạn sẽ không xử lý chính xác tất cả các trường hợp và người dùng sẽ tức giận với bạn khi phần mềm của bạn không hoạt động trong môi trường của họ.

25
Đây chính xác là những gì tôi cần. "ĐỪNG cố gắng đưa ra các bản hack ma thuật để làm cho Python có thể nhập mô-đun hoặc gói của bạn mà không cần người dùng thêm thư mục chứa nó vào đường dẫn nhập của họ." Tốt để biết!
Jack O'Connor

1
Điều này là, điều này không đề cập đến phần tài liệu quan trọng của một dự án nơi đặt nó.
lpapp

14
Nhầm lẫn về "đặt nguồn của bạn vào một thư mục có tên src hoặc lib. Điều này làm cho nó khó chạy mà không cài đặt." Những gì sẽ được cài đặt? Đây có phải là tên dir gây ra vấn đề, hoặc thực tế nó là một subir không?
Peter Ehrlich

4
"Một số người sẽ khẳng định rằng bạn nên phân phối các thử nghiệm của mình trong chính mô-đun của mình - tôi không đồng ý. Nó thường làm tăng sự phức tạp cho người dùng của bạn; nhiều bộ thử nghiệm thường yêu cầu phụ thuộc bổ sung và bối cảnh thời gian chạy." python-guide-pt-br.readthedocs.io/en/latest/wr/structure/ith
endolith

2
"Điều này làm cho nó khó chạy mà không cài đặt." - đó là điểm chính
Nick T

123

Kiểm tra nguồn mở một dự án Python đúng cách .

Hãy để tôi trích đoạn phần bố trí dự án của bài viết xuất sắc đó:

Khi thiết lập một dự án, bố cục (hoặc cấu trúc thư mục) rất quan trọng để có được quyền. Bố cục hợp lý có nghĩa là những người đóng góp tiềm năng không phải mất thời gian để săn lùng một đoạn mã; vị trí tập tin là trực quan. Vì chúng tôi đang xử lý một dự án hiện có, điều đó có nghĩa là bạn có thể sẽ cần phải di chuyển một số thứ xung quanh.

Hãy bắt đầu từ đầu. Hầu hết các dự án đều có một số tệp cấp cao nhất (như setup.py, README.md, tests.txt, v.v.). Sau đó, có ba thư mục mà mọi dự án nên có:

  • Một thư mục tài liệu chứa tài liệu dự án
  • Một thư mục có tên của dự án lưu trữ gói Python thực tế
  • Một thư mục thử nghiệm ở một trong hai nơi
    • Trong thư mục gói chứa mã kiểm tra và tài nguyên
    • Là một thư mục cấp cao nhất độc lập Để hiểu rõ hơn về cách tổ chức các tệp của bạn, đây là một ảnh chụp đơn giản về bố cục cho một trong các dự án của tôi, sandman:
$ pwd
~/code/sandman
$ tree
.
|- LICENSE
|- README.md
|- TODO.md
|- docs
|   |-- conf.py
|   |-- generated
|   |-- index.rst
|   |-- installation.rst
|   |-- modules.rst
|   |-- quickstart.rst
|   |-- sandman.rst
|- requirements.txt
|- sandman
|   |-- __init__.py
|   |-- exception.py
|   |-- model.py
|   |-- sandman.py
|   |-- test
|       |-- models.py
|       |-- test_sandman.py
|- setup.py

Như bạn có thể thấy, có một số tệp cấp cao nhất, thư mục docs (được tạo là một thư mục trống trong đó nhân sư sẽ đặt tài liệu đã tạo), thư mục sandman và thư mục kiểm tra dưới sandman.


4
Tôi làm điều này, nhưng hơn thế nữa: Tôi có một Makefile toplevel với mục tiêu 'env' tự động hóa 'virtualenv env; ./env/bin/pip cài đặt -r tests.txt; ./env/bin/python setup.py phát triển 'và cũng thường là mục tiêu' kiểm tra 'phụ thuộc vào env và cũng cài đặt các phụ thuộc kiểm tra và sau đó chạy py.test.
pjz

@pjz Bạn có thể vui lòng mở rộng ý tưởng của mình không? Bạn đang nói về việc đặt Makefileở cùng cấp độ với setup.py? Vì vậy, nếu tôi hiểu bạn make envtự động hóa chính xác việc tạo một cái mới venvvà cài đặt các gói vào nó ...?
St.Antario

@ St.Antario chính xác. Như đã đề cập, tôi thường có một mục tiêu 'thử nghiệm' để chạy thử nghiệm, và đôi khi một mục tiêu 'phát hành' nhìn vào thẻ hiện tại và xây dựng một bánh xe và gửi nó đến pypi.
pjz

32

"Cơ quan đóng gói Python" có một dự án mẫu:

https://github.com/pypa/sampleproject

Đây là một dự án mẫu tồn tại dưới dạng trợ giúp cho Hướng dẫn sử dụng bao bì và phân phối dự án của Hướng dẫn sử dụng bao bì Python.


+ xu hướng về root/src/*cấu trúc: github.com/pypa/sampleproject/commit/ từ
qrtLs

đối với cấu trúc dự án, recs cũng xem setuptools.readthedocs.io/_/doads/en/latest/pdf
qrtLs

19

Hãy thử bắt đầu dự án bằng cách sử dụng mẫu python_boilerplate . Nó chủ yếu tuân theo các thực tiễn tốt nhất (ví dụ như ở đây ), nhưng phù hợp hơn trong trường hợp bạn thấy mình sẵn sàng chia dự án của bạn thành nhiều quả trứng vào một lúc nào đó (và tin tôi, với bất cứ điều gì trừ các dự án đơn giản nhất, bạn sẽ làm. tình huống phổ biến là bạn phải sử dụng phiên bản sửa đổi cục bộ của thư viện của người khác).

  • Bạn đặt nguồn ở đâu?

    • Đối với các dự án lớn, thật hợp lý khi chia nguồn thành nhiều quả trứng. Mỗi quả trứng sẽ là một bố cục thiết lập riêng biệt bên dưới PROJECT_ROOT/src/<egg_name>.
  • Nơi nào bạn đặt các kịch bản khởi động ứng dụng?

    • Tùy chọn lý tưởng là có tập lệnh khởi động ứng dụng được đăng ký như một entry_pointtrong những quả trứng.
  • Nơi nào bạn đặt dự án IDE cruft?

    • Phụ thuộc vào IDE. Nhiều người trong số họ giữ công cụ của họ PROJECT_ROOT/.<something>trong thư mục gốc của dự án, và điều này là tốt.
  • Nơi nào bạn đặt các bài kiểm tra đơn vị / chấp nhận?

    • Mỗi quả trứng có một bộ thử nghiệm riêng, được giữ trong PROJECT_ROOT/src/<egg_name>/teststhư mục của nó . Cá nhân tôi thích sử dụng py.testđể chạy chúng.
  • Nơi nào bạn đặt dữ liệu không phải Python như tệp cấu hình?

    • Nó phụ thuộc. Có thể có nhiều loại dữ liệu không phải là Python.
      • "Tài nguyên" , tức là dữ liệu phải được đóng gói trong một quả trứng. Dữ liệu này đi vào thư mục trứng tương ứng, ở đâu đó trong không gian tên gói. Nó có thể được sử dụng thông qua pkg_resourcesgói từ setuptoolshoặc từ Python 3.7 qua importlib.resourcesmô-đun từ thư viện chuẩn.
      • "Tệp cấu hình" , tức là các tệp không phải Python được coi là bên ngoài đối với các tệp nguồn dự án, nhưng phải được khởi tạo với một số giá trị khi ứng dụng bắt đầu chạy. Trong quá trình phát triển, tôi thích giữ các tập tin như vậy PROJECT_ROOT/config. Để triển khai có thể có nhiều lựa chọn khác nhau. Trên Windows người ta có thể sử dụng %APP_DATA%/<app-name>/config, trên Linux, /etc/<app-name>hoặc /opt/<app-name>/config.
      • Các tệp được tạo , tức là các tệp có thể được tạo hoặc sửa đổi bởi ứng dụng trong khi thực thi. Tôi muốn giữ chúng trong PROJECT_ROOT/varquá trình phát triển và /vartrong quá trình triển khai Linux.
  • Nơi nào bạn đặt các nguồn không phải Python như C ++ cho các mô đun mở rộng nhị phân pyd / so?
    • Vào PROJECT_ROOT/src/<egg_name>/native

Tài liệu thường đi vào PROJECT_ROOT/dochoặc PROJECT_ROOT/src/<egg_name>/doc(điều này phụ thuộc vào việc bạn coi một số trứng là một dự án lớn riêng biệt). Một số cấu hình bổ sung sẽ có trong các tập tin như PROJECT_ROOT/buildout.cfgPROJECT_ROOT/setup.cfg.


Cảm ơn cho một câu trả lời tuyệt vời! Bạn đã làm rõ nhiều điều cho tôi! Tôi chỉ có một câu hỏi: Trứng có thể được lồng không?
Shookie

Không, bạn không thể "làm tổ" trứng theo nghĩa lưu trữ các tệp .egg trong các tệp .egg khác và hy vọng việc này sẽ được sử dụng nhiều [trừ khi bạn gặp phải điều gì đó thực sự kỳ lạ]. Mặc dù vậy, những gì bạn có thể làm là tạo ra những quả trứng "ảo" - các gói trống không cung cấp bất kỳ mã hữu ích nào, nhưng liệt kê các gói khác trong danh sách phụ thuộc của chúng. Bằng cách này, khi người dùng cố gắng cài đặt một gói như vậy, anh ta sẽ cài đặt đệ quy nhiều trứng phụ thuộc.
KT.

@KT bạn có thể giải thích một chút về cách bạn xử lý dữ liệu được tạo không? Cụ thể, làm thế nào để bạn (trong mã) phân biệt giữa phát triển và triển khai? Tôi tưởng tượng bạn có một số base_data_locationbiến, nhưng làm thế nào để bạn thiết lập nó một cách thích hợp?
cmyr

1
Tôi đoán bạn đang nói về "dữ liệu thời gian chạy" - thứ mà mọi người thường đặt dưới / var / packagename hoặc ~ / .packagename / var, hoặc whatnot. Hầu hết thời gian những lựa chọn đó là đủ mặc định mà người dùng của bạn không quan tâm để thay đổi. Nếu bạn muốn cho phép điều chỉnh hành vi này, các tùy chọn khá phong phú và tôi không nghĩ có một cách phù hợp nhất - tất cả thực hành tốt nhất. Các lựa chọn điển hình: a) ~ / .packagename / configfile, b) export MY_PACKAGE_CONFIG = / path / to / configfile c) tùy chọn dòng lệnh hoặc tham số chức năng d) kết hợp các tùy chọn đó.
KT.

Lưu ý rằng thông thường có một lớp Cấu hình đơn lẻ ở đâu đó, nơi xử lý logic tải cấu hình yêu thích của bạn và thậm chí có thể cho phép người dùng sửa đổi cài đặt khi chạy. Tuy nhiên, nói chung, tôi nghĩ rằng đây là một vấn đề đáng giá một câu hỏi riêng biệt (có thể đã được hỏi trước đây ở đâu đó).
KT.

15

Theo kinh nghiệm của tôi, đó chỉ là vấn đề lặp đi lặp lại. Đặt dữ liệu và mã của bạn bất cứ nơi nào bạn nghĩ rằng họ đi. Rất có thể, dù sao bạn cũng sẽ sai. Nhưng một khi bạn có được một ý tưởng tốt hơn về chính xác mọi thứ sẽ hình thành như thế nào, bạn sẽ ở một vị trí tốt hơn nhiều để đưa ra những loại dự đoán này.

Theo như các nguồn mở rộng, chúng tôi có một thư mục Code trong thân cây chứa một thư mục cho python và một thư mục cho các ngôn ngữ khác nhau. Cá nhân, tôi có xu hướng thử đưa bất kỳ mã mở rộng nào vào kho lưu trữ của riêng mình vào lần tới.

Như đã nói, tôi quay trở lại điểm ban đầu của mình: đừng tạo ra một thỏa thuận quá lớn từ nó. Đặt nó ở một nơi nào đó dường như làm việc cho bạn. Nếu bạn tìm thấy một cái gì đó không hoạt động, nó có thể (và nên) được thay đổi.


Vâng. Tôi cố gắng trở thành "Pythonic" về nó: rõ ràng là tốt hơn ngầm .. Những người thừa kế thư mục được đọc / kiểm tra nhiều hơn so với chúng được viết. V.v ..
eric

10

Dữ liệu không phải python được gói tốt nhất bên trong các mô-đun Python của bạn bằng cách sử dụng package_datahỗ trợ trong setuptools . Một điều tôi đặc biệt khuyên bạn là sử dụng các gói không gian tên để tạo các không gian tên được chia sẻ mà nhiều dự án có thể sử dụng - giống như quy ước Java về việc đặt các gói vào com.yourcompany.yourproject(và có thể có một com.yourcompany.utilskhông gian tên được chia sẻ ).

Phân nhánh lại và sáp nhập, nếu bạn sử dụng một hệ thống kiểm soát nguồn đủ tốt, nó sẽ xử lý việc hợp nhất ngay cả thông qua đổi tên; Bazaar đặc biệt tốt trong việc này.

Trái với một số câu trả lời khác ở đây, tôi +1 về việc có một srcthư mục cấp cao nhất (có doctestthư mục bên cạnh). Các quy ước cụ thể cho cây thư mục tài liệu sẽ thay đổi tùy thuộc vào những gì bạn đang sử dụng; Sphinx , ví dụ, có các quy ước riêng mà công cụ khởi động nhanh của nó hỗ trợ.

Xin vui lòng, hãy tận dụng setuptools và pkg_resource; điều này giúp các dự án khác dễ dàng hơn nhiều dựa vào các phiên bản mã cụ thể của bạn (và cho nhiều phiên bản được cài đặt đồng thời với các tệp không phải mã khác nhau, nếu bạn đang sử dụng package_data).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.