2013-08-06 26 views
21

Tôi đang viết một họ các kịch bản Python trong một dự án; mỗi tập lệnh nằm trong thư mục con của dự án, như vậy:Python: chia sẻ mã chung giữa một nhóm các kịch bản

projectroot 
    | 
    |- subproject1 
    | | 
    | |- script1.main.py 
    | `- script1.merger.py 
    | 
    |- subproject2 
    | | 
    | |- script2.main.py 
    | |- script2.matcher.py 
    | `- script2.merger.py 
    | 
    `- subproject3 
     | 
     |- script3.main.py 
     |- script3.converter.py 
     |- script3.matcher.py 
     `- script3.merger.py 

Bây giờ, một số tập lệnh chia sẻ một số mã. Mã chia sẻ tốt nhất được coi là một phần của bản thân dự án, chứ không phải là một cái gì đó tôi sẽ biên dịch một cách riêng biệt và tạo ra một thư viện, hoặc thả vào một PYTHONPATH trên toàn trang web. Tôi có thể đặt mã đó ở những nơi khác nhau, chẳng hạn như trong thư mục projectroot, hoặc trong một thư mục con của projectroot được gọi là common (có thể). Tuy nhiên, hầu hết các cách tôi đã nghĩ đến cho đến nay liên quan đến việc thực hiện các gói trong các tiểu dự án của tôi với các tệp rỗng __init__.py và sử dụng nhập khẩu tương đối (hoặc lộn xộn với sys.path trong mỗi tiểu dự án. xung quanh gia đình này các script chạy afoul của cảnh báo sau đây từ từ chối PEP-3122:

Attention! This PEP has been rejected. Guido views running scripts within a package as an anti-pattern.

Nếu kịch bản trong vòng một gói là chống patternish, làm thế nào tôi có thể thiết lập những điều theo một cách mà giữ các mã thông thường trong cùng một Hoặc là một mô-đun và gói dựa trên hệ thống chấp nhận được ở đây? Đó là cách tiếp cận sạch nhất? (FWIW tôi muốn t o có một tệp chẳng hạn như shared.py hoặc common.py trong thư mục gốc của dự án, thay vì tạo thư mục tiện ích là anh chị em cho các tiểu dự án "thực".)

+1

tôi tin rằng django sử dụng một điểm vào tập trung 'manage.py' để chạy tất cả các tập lệnh của nó. Làm một cái gì đó như thế này có thể cho phép bạn biến 'subprojectX' thành các gói và xử lý việc nhập tập trung bên trong tập lệnh" 'manage.py'" (điểm vào). Như các gói, tôi tin rằng, nó sẽ dễ dàng hỗ trợ một mô-đun 'phổ biến', nơi chức năng chia sẻ của bạn có thể hoạt động. – dm03514

+1

Tôi tin rằng phải là [PEP-3122] (https://www.python.org/dev/peps/pep-3122/), không phải PEP-32122. – user1071847

Trả lời

19

Tôi khuyên bạn nên đặt các tập lệnh "trình khởi chạy" tầm thường ở cấp cao nhất của dự án và làm cho mỗi thư mục dự án thành các gói. Các mô-đun trong các gói có thể nhập mỗi mã khác hoặc mã thông dụng có thể được tính vào gói common.

Đây là những gì cấu trúc sẽ như thế nào, nếu chúng ta giả định khác nhau merger module có thể được refactored thành một phiên bản chia sẻ:

projectroot 
    |- script1.py # launcher scripts, see below for example code 
    |- script2.py 
    |- script3.py 
    | 
    |- common 
    | |- __init__.py 
    | |- merger.py # from other packages, use from ..common import merger to get this 
    | 
    |- subproject1 
    | |- __init__.py # this can be empty 
    | |- script1_main.py 
    | 
    |- subproject2 
    | |- __init__.py 
    | |- script2_main.py 
    | |- script2_matcher.py 
    | 
    |- subproject3 
     |- __init__.py 
     |- script3_main.py 
     |- script3_converter.py 
     |- script3_matcher.py 

Các kịch bản phóng có thể rất đơn giản:

from subproject1 import script1_main 

if __name__ == "__main__": 
    script1_main.main() 

Đó là, tất cả những gì nó làm là nhập khẩu mô-đun "scriptN_main" thích hợp và chạy một hàm trong đó. Sử dụng một tập lệnh đơn giản cũng có thể có một số lợi ích nhỏ cho tốc độ khởi động script, vì mô-đun main có thể có bytecode được biên dịch được lưu vào tệp .pyc, trong khi các tập lệnh không bao giờ được lưu trong bộ nhớ cache.

Lưu ý: Tôi đã đổi tên mô-đun của bạn, hoán đổi _ ký tự cho các ký tự .. Bạn không thể có số . trong số nhận dạng (chẳng hạn như tên mô-đun), vì Python hy vọng nó sẽ cho biết quyền truy cập thuộc tính. Điều đó có nghĩa là các mô-đun đó không bao giờ có thể được nhập. (Tôi đoán rằng đây là một tạo tác của các tập tin ví dụ duy nhất, không phải là một cái gì đó mà bạn có trong mã thực sự của bạn.)

+0

Có vẻ tốt, nhưng tôi có thể nói gì bên trong 'subproject1.script1_main.py' để truy cập 'common'? Tôi đã thử 'import common' nhưng nhận được' File 'movie/main.py ", dòng 1, trong import ImportError phổ biến: Không có module nào được đặt tên phổ biến' Tôi không muốn đặt' sys.path' theo cách thủ công. Tui bỏ lỡ điều gì vậy? –

+0

Tôi nghĩ rằng 'import ..common' sẽ hoạt động (một lần nhập tương đối rõ ràng). Đảm bảo rằng bạn đang chạy tập lệnh ở cấp cao nhất thay vì chạy trực tiếp các tệp tiểu dự án hoặc có thể không nhận ra tệp đó trong gói (bạn sẽ gặp lỗi về phần '..' của quá trình nhập trong đó trường hợp). – Blckknght

+1

1. Thư mục "chung" cần tệp '__init __. Py' bên trong. 2. Lệnh 'import ..common', trong khi' from .. import common' là đúng. Nó yêu cầu projectroot cũng chứa '__init __. Py' và cũng được nhập như gói cha. 3. Nếu bạn không import projectroot nhưng bạn chạy một script trong đó, bạn có thể dễ dàng 'import common', bởi vì '.' thư mục của tập lệnh được tự động thêm vào đường dẫn python khi khởi động. – hynekcer

0

Tuỳ chọn của tôi sẽ là một "thùng" hoặc "tập lệnh" riêng biệt thư mục, với các tiểu dự án là thư viện/gói:

projectroot 
    | 
    |- scripts 
    | 
    |- lib 
    | | 
    | `- matcher.py 
    | `- merger.py 
    | `- subproject1 
    | `- subproject2 
    | `- subproject3 

Ý tưởng là tập lệnh của bạn có thể tham chiếu bất kỳ dự án con nào cần thiết như gói thông thường. Và các tiểu dự án của bạn cũng có thể tham chiếu lẫn nhau với các mục nhập.

Sau đó, bạn có thể có tập lệnh chính hoặc được chia sẻ để thiết lập các gói dự án con cho bạn, nếu điều đó có ích.

+0

Tôi thích bộ phận đó nhưng tôi đã chỉnh sửa câu trả lời của mình để cho thấy lý do đưa mỗi kịch bản vào một tiểu dự án riêng biệt để bắt đầu - đó là vì mỗi "tập lệnh" có nhiều phần. Tôi muốn có một số mã thư viện được chia sẻ - các hàm (và các lớp) có thể sử dụng được bởi mỗi kịch bản trong số các tiểu dự án khác nhau. –

+0

Tôi nghĩ rằng mã chia sẻ nên đi vào thư mục gốc của libs. Vui lòng xem chỉnh sửa của tôi. –

+1

Matt - Làm cách nào để nhập bất kỳ thứ gì từ 'lib' trong tập lệnh dưới' tập lệnh'? Bạn sẽ không thể sử dụng nhập khẩu tương đối, vì chúng không phải là mô-đun kỹ thuật. –

0

Please use setuptools để phân phối cả hai kịch bản thư viện:

ví dụ

from setuptools import setup 

setup(
    # other arguments here... (e.g. packages/package_dir) 
    entry_points = { 
     'console_scripts': [ 
      'script1 = subproject1.script1:main', 
      'script2 = subproject2.script2:main', 
     ], 
    } 
) 

Nếu bạn có thể viết tất cả mã là thư viện và không cần các mô-đun riêng để có điểm vào thì đây là công cụ dành cho bạn. Nếu bạn có kịch bản, điều đó cũng tốt, nhưng bạn sẽ cần một hàm main bạn có thể tham khảo (xem ví dụ ở trên)

Các vấn đề liên quan