Nhiều con trăn argparse

Các công cụ Giao diện dòng lệnh [cli] là bánh mì và bơ của các nhà khoa học dữ liệu khi yêu cầu của họ vượt xa những gì máy tính xách tay Jupyter có thể cung cấp. Python argparse từ thư viện chuẩn là công cụ đầu tiên chúng tôi gặp trong hành trình Python của mình để xây dựng các giao diện như vậy một cách dễ dàng. Tuy nhiên, mặc dù có thể dễ dàng xây dựng một cli nhỏ dễ sử dụng với argparse chỉ bằng ba phương thức của lớp ArgumentParser, nhưng cần có một số kế hoạch bổ sung khi giao diện phát triển và trở nên phức tạp hơn. Trong bài viết này, chúng tôi khám phá các phương pháp để chia cli của chúng tôi thành các lệnh phụ riêng biệt với các đối số có thể được tải trong thời gian chạy, cũng như cách kết hợp chúng với các mẫu thiết kế để mở rộng nó dễ dàng hơn

Nhiều tiểu ban

Hầu hết các công cụ cli cung cấp nhiều lệnh, mỗi lệnh có bộ đối số riêng. Hãy lấy git làm ví dụ, sau đây là danh sách tất cả các lệnh git bạn có thể chạy

$ git --help
[...]
These are common Git commands used in various situations:

start a working area [see also: git help tutorial]
clone Clone a repository into a new directory
init Create an empty Git repository or reinitialize an existing one

work on the current change [see also: git help everyday]
add Add file contents to the index
mv Move or rename a file, a directory, or a symlink
restore Restore working tree files
rm Remove files from the working tree and from the index

examine the history and state [see also: git help revisions]
bisect Use binary search to find the commit that introduced a bug
diff Show changes between commits, commit and working tree, etc
grep Print lines matching a pattern
log Show commit logs
show Show various types of objects
status Show the working tree status

grow, mark and tweak your common history
branch List, create, or delete branches
commit Record changes to the repository
merge Join two or more development histories together
rebase Reapply commits on top of another base tip
reset Reset current HEAD to the specified state
switch Switch branches
tag Create, list, delete or verify a tag object signed with GPG

collaborate [see also: git help workflows]
fetch Download objects and refs from another repository
pull Fetch from and integrate with another repository or a local branch
push Update remote refs along with associated objects

và đây là những lập luận cho một số trong số họ

$ git add --helpSYNOPSISgit add [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p] [--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]] [--sparse] [--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing] [-renormalize] [--chmod=[+|-]x] [--pathspec-from-file= [--pathspec-file-nul]] [--] […​]$ git commit --helpSYNOPSISgit commit [-a | --interactive | --patch] [-s] [-v] [-u] [--amend]
[--dry-run] [[-c | -C | --squash] | --fixup [[amend|reword]:]]]
[-F | -m ] [--reset-author] [--allow-empty]
[--allow-empty-message] [--no-verify] [-e] [--author=]
[--date=] [--cleanup=] [--[no-]status]
[-i | -o] [--pathspec-from-file= [--pathspec-file-nul]]
[[--trailer [[=|:]]]…​] [-S[]]
[--] […​]

Bạn có thể thấy rằng chúng trùng nhau nhưng không giống nhau. Nếu bạn cố gắng sử dụng một lệnh có đối số từ một lệnh khác thì rõ ràng là nó sẽ bị lỗi

$ git add --reset-author
error: unknown option `reset-author'
usage: git add [] [--] ...

Giả sử bạn đang viết một công cụ cli cho công việc khoa học dữ liệu của mình bằng Python, có thể bạn đang viết một công cụ để giải quyết các vấn đề trong miền của mình bằng học sâu

Chọn công cụ học sâu của bạn

Tại sao công cụ của bạn có thể phụ thuộc vào cấu trúc nhóm của tổ chức bạn

hướng tới khoa học dữ liệu. com

thì rất có thể bạn sẽ viết cli của mình bằng argparse từ thư viện chuẩn, vì nó có thể không phải là tốt nhất cho mục đích này nhưng chắc chắn đây là cái đầu tiên bạn học

Xây dựng một trình phân tích cú pháp đối số để sử dụng các lệnh phụ khá đơn giản và chúng tôi sẽ minh họa bằng một ví dụ. Chúng tôi có ứng dụng ví dụ của mình, với các lệnh đào tạo và suy luận, mỗi lệnh có các đối số khác nhau có thể là bắt buộc hoặc tùy chọn. Mã ví dụ có sẵn tại https. //github. com/mattiadg/example-cli và sao chép ở đây để thuận tiện

# src/sub_commands.pyimport argparse

from commands import train, infer


if __name__ == "__main__":
parser = argparse.ArgumentParser["Example v2"]
subparsers = parser.add_subparsers[help="Sub-commands help"]

parser_train = subparsers.add_parser["train", help="Train a model"]
parser_train.add_argument[
"--model",
"-m",
required=True,
help="name of the deep learning architecture to use",
]
parser_train.add_argument[
"--save_model_path",
required=True,
help="Path to the directory where to save the model",
]
parser_train.add_argument[
"--dropout",
type=float,
default=0.1,
help="Dropout value, equal for each value",
]
parser_train.add_argument[
"--batch_size", type=int, help="Batch size during training"
]
parser_train.set_defaults[func=train]

parser_infer = subparsers.add_parser["infer", help="Use a model for inference"]
parser_infer.add_argument[
"--model_path", required=True, help="Path to the model to use for inference"
]
parser_infer.add_argument[
"--batch_size", type=int, help="Batch size during inference"
]
parser_infer.set_defaults[func=infer]

args = parser.parse_args[]
args.func[args]

Các Subparsers từ argparse sẽ thực hiện công việc. Trước tiên, chúng tôi cần tuyên bố rằng chúng tôi muốn sử dụng các bộ phân tích con với

subparsers = parser.add_subparsers[help="Sub-commands help"]

Sau đó, chúng ta khai báo các bộ phân tích con cho các lệnh “train” và “infer” bằng phương thức add_parser[]

parser_train = subparsers.add_parser["train", help="Train a model"]
parser_infer = subparsers.add_parser["infer", help="Use a model for inference"]

Sau đó, các trình phân tích cú pháp con sẽ hoạt động chính xác như bất kỳ trình phân tích cú pháp nào khác. Hãy kiểm tra cli của chúng tôi

$ python .\src\sub_commands.py train -m transformer --save_model_path $HOME/my_model_path/
Training model with:
model=transformer
save_model_path=/home/me/my_model_path/
dropout=0.1
batch_size=None
func=

Và nó sẽ không chấp nhận các đối số cụ thể để suy luận

$ python .\src\correct.py train -m transformer --save_model_path $HOME/my_model_path/ --model_path .
usage: Example v2 [-h] {train,infer} ...
Example v2: error: unrecognized arguments: --model_path .

Nhưng — model_path hoạt động chính xác khi sử dụng lệnh “suy luận”

$ python .\src\correct.py infer --model_path $HOME/my_model_path/
Inferring with model with:
model_path=/home/me/my_model_path/
batch_size=None
func=
Đối số động

Bây giờ, chúng tôi muốn chỉ định một số đối số cho các mô hình mà chúng tôi muốn đào tạo. Trong ví dụ của chúng tôi, chúng tôi có hai mô hình khác nhau, máy biến áp và lstm. Lstm có các tham số là kích thước của vectơ đầu ra và kích thước của vectơ trạng thái. Máy biến áp có kích thước của các lớp con và kích thước khác nhau cho lớp con chuyển tiếp lớn. Cả hai loại mô hình đều có số lớp làm đối số

Tôi không tìm thấy cách đính kèm trình phân tích cú pháp phụ vào giá trị của một đối số được đặt tên như mô hình, vì vậy chúng tôi phải đi theo một lộ trình khác

Đầu tiên, để giữ cho mã trình phân tích cú pháp có thể quản lý được, chúng tôi không muốn tất cả các đối số hiển thị trong cùng một tệp chính. Chúng tôi sẽ giữ các đối số mới trong các tệp nơi chúng tôi xác định các mô hình tương ứng bằng cách sử dụng hàm lấy trình phân tích cú pháp làm đầu vào và thêm các đối số mới

Đây là giao diện của Lstm

# src/models/lstm.pydef add_arguments[parser]:
parser_lstm = parser.add_argument_group["lstm"]
parser_lstm.add_argument["--num_layers", type=int, help="Number of LSTM layers"]
parser_lstm.add_argument["--forward_size", type=int, help="Number of units in the forward propagation"]
parser_lstm.add_argument["--state_size", type=int, help="Number of units for the state vector"]

Và sau đây cho máy biến áp

$ git add --helpSYNOPSISgit add [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p] [--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]] [--sparse] [--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing] [-renormalize] [--chmod=[+|-]x] [--pathspec-from-file= [--pathspec-file-nul]] [--] […​]$ git commit --helpSYNOPSISgit commit [-a | --interactive | --patch] [-s] [-v] [-u] [--amend]
[--dry-run] [[-c | -C | --squash] | --fixup [[amend|reword]:]]]
[-F | -m ] [--reset-author] [--allow-empty]
[--allow-empty-message] [--no-verify] [-e] [--author=]
[--date=] [--cleanup=] [--[no-]status]
[-i | -o] [--pathspec-from-file= [--pathspec-file-nul]]
[[--trailer [[=|:]]]…​] [-S[]]
[--] […​]
0

Bây giờ, chúng ta không thể chỉ thêm tất cả các đối số từ các mô hình khác nhau khi khởi động, vì chúng có các tên xung đột [ — num_layers] sẽ khiến trình phân tích cú pháp gặp sự cố. Trong tập tin trùng lặp. py, chúng tôi sao chép mã hoạt động trong phần trước và thêm các đối số từ hai mô hình, bạn có thể tìm thấy tại https. //github. com/mattiadg/example-cli/blob/main/src/duplicate_arguments. py

$ git add --helpSYNOPSISgit add [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p] [--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]] [--sparse] [--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing] [-renormalize] [--chmod=[+|-]x] [--pathspec-from-file= [--pathspec-file-nul]] [--] […​]$ git commit --helpSYNOPSISgit commit [-a | --interactive | --patch] [-s] [-v] [-u] [--amend]
[--dry-run] [[-c | -C | --squash] | --fixup [[amend|reword]:]]]
[-F | -m ] [--reset-author] [--allow-empty]
[--allow-empty-message] [--no-verify] [-e] [--author=]
[--date=] [--cleanup=] [--[no-]status]
[-i | -o] [--pathspec-from-file= [--pathspec-file-nul]]
[[--trailer [[=|:]]]…​] [-S[]]
[--] […​]
1

Nếu bây giờ chúng ta chạy

$ git add --helpSYNOPSISgit add [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p] [--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]] [--sparse] [--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing] [-renormalize] [--chmod=[+|-]x] [--pathspec-from-file= [--pathspec-file-nul]] [--] […​]$ git commit --helpSYNOPSISgit commit [-a | --interactive | --patch] [-s] [-v] [-u] [--amend]
[--dry-run] [[-c | -C | --squash] | --fixup [[amend|reword]:]]]
[-F | -m ] [--reset-author] [--allow-empty]
[--allow-empty-message] [--no-verify] [-e] [--author=]
[--date=] [--cleanup=] [--[no-]status]
[-i | -o] [--pathspec-from-file= [--pathspec-file-nul]]
[[--trailer [[=|:]]]…​] [-S[]]
[--] […​]
2

Chúng tôi nhận được lỗi dự kiến

$ git add --helpSYNOPSISgit add [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p] [--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]] [--sparse] [--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing] [-renormalize] [--chmod=[+|-]x] [--pathspec-from-file= [--pathspec-file-nul]] [--] […​]$ git commit --helpSYNOPSISgit commit [-a | --interactive | --patch] [-s] [-v] [-u] [--amend]
[--dry-run] [[-c | -C | --squash] | --fixup [[amend|reword]:]]]
[-F | -m ] [--reset-author] [--allow-empty]
[--allow-empty-message] [--no-verify] [-e] [--author=]
[--date=] [--cleanup=] [--[no-]status]
[-i | -o] [--pathspec-from-file= [--pathspec-file-nul]]
[[--trailer [[=|:]]]…​] [-S[]]
[--] […​]
3

Rõ ràng là chúng ta cần một cách để thêm các đối số vào trình phân tích cú pháp của mình một cách linh hoạt, nghĩa là, trong thời gian chạy theo đầu vào từ người dùng của chúng ta. Tuy nhiên, không có phương thức nào của ArgumentParser cho phép chúng tôi thực hiện trực tiếp và chúng tôi cần sử dụng một mẹo nhỏ

Chúng ta cần phân tích cú pháp các đối số hai lần, lần đầu tiên để lấy giá trị mô hình, sau đó tải các đối số tương ứng và sau đó với lần phân tích cú pháp thứ hai, chúng ta có thể nhận được các đối số dành riêng cho lệnh

Để làm điều này, chúng ta cần trình phân tích cú pháp bỏ qua các đối số do người dùng chèn vào mà chưa được thêm làm đối số. May mắn thay, parse_known_args[] thực hiện chính xác điều này

$ git add --helpSYNOPSISgit add [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p] [--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]] [--sparse] [--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing] [-renormalize] [--chmod=[+|-]x] [--pathspec-from-file= [--pathspec-file-nul]] [--] […​]$ git commit --helpSYNOPSISgit commit [-a | --interactive | --patch] [-s] [-v] [-u] [--amend]
[--dry-run] [[-c | -C | --squash] | --fixup [[amend|reword]:]]]
[-F | -m ] [--reset-author] [--allow-empty]
[--allow-empty-message] [--no-verify] [-e] [--author=]
[--date=] [--cleanup=] [--[no-]status]
[-i | -o] [--pathspec-from-file= [--pathspec-file-nul]]
[[--trailer [[=|:]]]…​] [-S[]]
[--] […​]
4

trong đó parser_train là trình phân tích cú pháp con cho lệnh train, như trước đây và công cụ thay đổi trò chơi là dòng này

$ git add --helpSYNOPSISgit add [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p] [--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]] [--sparse] [--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing] [-renormalize] [--chmod=[+|-]x] [--pathspec-from-file= [--pathspec-file-nul]] [--] […​]$ git commit --helpSYNOPSISgit commit [-a | --interactive | --patch] [-s] [-v] [-u] [--amend]
[--dry-run] [[-c | -C | --squash] | --fixup [[amend|reword]:]]]
[-F | -m ] [--reset-author] [--allow-empty]
[--allow-empty-message] [--no-verify] [-e] [--author=]
[--date=] [--cleanup=] [--[no-]status]
[-i | -o] [--pathspec-from-file= [--pathspec-file-nul]]
[[--trailer [[=|:]]]…​] [-S[]]
[--] […​]
5

cần nhận hai giá trị trả về vì parse_known_args trả về Tuple chứa các đối số được phân tích cú pháp ở vị trí đầu tiên và tất cả phần còn lại ở vị trí thứ hai dưới dạng danh sách. Đây là những gì chúng tôi nhận được bằng cách in kết quả của trình phân tích cú pháp. parse_known_args[]

$ git add --helpSYNOPSISgit add [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p] [--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]] [--sparse] [--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing] [-renormalize] [--chmod=[+|-]x] [--pathspec-from-file= [--pathspec-file-nul]] [--] […​]$ git commit --helpSYNOPSISgit commit [-a | --interactive | --patch] [-s] [-v] [-u] [--amend]
[--dry-run] [[-c | -C | --squash] | --fixup [[amend|reword]:]]]
[-F | -m ] [--reset-author] [--allow-empty]
[--allow-empty-message] [--no-verify] [-e] [--author=]
[--date=] [--cleanup=] [--[no-]status]
[-i | -o] [--pathspec-from-file= [--pathspec-file-nul]]
[[--trailer [[=|:]]]…​] [-S[]]
[--] […​]
6

Với sửa đổi mới nhất, cuối cùng chúng tôi cũng nhận được kết quả như mong đợi

$ git add --helpSYNOPSISgit add [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p] [--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]] [--sparse] [--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing] [-renormalize] [--chmod=[+|-]x] [--pathspec-from-file= [--pathspec-file-nul]] [--] […​]$ git commit --helpSYNOPSISgit commit [-a | --interactive | --patch] [-s] [-v] [-u] [--amend]
[--dry-run] [[-c | -C | --squash] | --fixup [[amend|reword]:]]]
[-F | -m ] [--reset-author] [--allow-empty]
[--allow-empty-message] [--no-verify] [-e] [--author=]
[--date=] [--cleanup=] [--[no-]status]
[-i | -o] [--pathspec-from-file= [--pathspec-file-nul]]
[[--trailer [[=|:]]]…​] [-S[]]
[--] […​]
7

Và bước còn thiếu chỉ là hàm load_model_args[] tải các đối số cho mô hình chính xác. Đây rồi

$ git add --helpSYNOPSISgit add [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p] [--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]] [--sparse] [--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing] [-renormalize] [--chmod=[+|-]x] [--pathspec-from-file= [--pathspec-file-nul]] [--] […​]$ git commit --helpSYNOPSISgit commit [-a | --interactive | --patch] [-s] [-v] [-u] [--amend]
[--dry-run] [[-c | -C | --squash] | --fixup [[amend|reword]:]]]
[-F | -m ] [--reset-author] [--allow-empty]
[--allow-empty-message] [--no-verify] [-e] [--author=]
[--date=] [--cleanup=] [--[no-]status]
[-i | -o] [--pathspec-from-file= [--pathspec-file-nul]]
[[--trailer [[=|:]]]…​] [-S[]]
[--] […​]
8

Đây là một triển khai cơ bản, chỉ lấy tên mô hình, nhập mô-đun có cùng tên bên trong gói “mô hình”, sau đó gọi hàm add_arguments trong đó [hiển thị ở trên] trên trình phân tích cú pháp mà nó nhận làm đầu vào

Điều còn thiếu ở đây là danh sách các mẫu có sẵn. Nếu được đặt một tên mô hình ngẫu nhiên, mã của chúng tôi sẽ bị lỗi vì không thể tìm thấy mô-đun tương ứng. Một cách khác là thêm vào sổ đăng ký trước tất cả các hàm add_arguments[] từ tất cả các mô hình có tên tương ứng. Sau đó, chúng tôi có thể cung cấp các tùy chọn này dưới dạng “lựa chọn” trong đối số — mô hình. Điều này có thể dễ dàng thực hiện bằng cách sử dụng mẫu Đăng ký mà tôi đã mô tả trong bài viết trước

Đa hình Python với thanh ghi. Mẫu Python

Tìm hiểu một mẫu để cô lập các gói trong khi mở rộng các chức năng của mã Python của bạn

hướng tới khoa học dữ liệu. com

Tại sao chúng ta sử dụng mẫu này dường như làm tăng thêm độ phức tạp cho mã? . Sau đó, mã trở nên phức tạp vốn có và tốt hơn là tách biệt các thay đổi bằng cách có một điểm nhập duy nhất [đăng ký] cho từng danh mục và chỉ tạo giao diện mã cụ thể với mã chung. Ngoài ra, chức năng chính sẽ chỉ giao tiếp với mã di truyền [load_model args, trình tối ưu hóa tải args, v.v.] và cơ sở mã trở nên dễ suy luận hơn về

Sự kết luận

Trong bài viết này, chúng ta đã thấy cách xây dựng clis có độ phức tạp tăng dần bằng cách sử dụng argparse từ thư viện chuẩn Python. Chúng tôi đã bắt đầu bằng cách đơn giản là sử dụng các trình phân tích con để thêm các lệnh phụ vào chương trình của mình, sau đó chúng tôi đã xem cách sử dụng parse_know_args để tải các đối số mới sau đó theo lựa chọn của người dùng. Cuối cùng, chúng tôi đã thảo luận về cách mẫu Đăng ký có thể giúp đạt được sự cô lập các mối quan tâm đối với trình phân tích cú pháp của chúng tôi

Như một nhận xét mới nhất, nếu bạn quan tâm đến việc phát triển các công cụ cli, hãy cân nhắc sử dụng các thư viện cung cấp nhiều tính năng hơn argparse, chẳng hạn như cloup

Tư cách thành viên trung bình

Bạn có thích bài viết của tôi và đang xem xét đăng ký Tư cách thành viên trung bình để có quyền truy cập không giới hạn vào các bài viết không?

Nếu bạn quyết định đăng ký thông qua liên kết này, bạn sẽ hỗ trợ tôi thông qua đăng ký của bạn mà không phải trả thêm phí cho bạn https. //vừa phải. com/@mattiadigangi/thành viên

Chủ Đề