ここで説明するテクニックはargparseモジュールの公式ドキュメントにあります(私はPython 2.7を使用しています)。新しいものは何も発明しませんでした。 プログラムの構造を改善し、次の問題を解決できます。
- 簡潔なディスパッチを使用して、特定のコマンドラインパラメーターに応答して特定の関数を呼び出す。
- ユーザー入力の処理と検証のカプセル化。
このメモを書く動機は、ほぼこの問題のトースターでの議論でした:
コマンドラインパラメータに応じて特定の関数を呼び出す方法そして精神でそれに答える
argparseとif / elifを使用します
sys.argvに目を向ける
テストとして、パラメーターの2つのブランチを持つ球状のスクリプトを使用します。
userdb.py append <username> <age> userdb.py show <userid>
最初のトリックで達成できる目標は次のとおりです。
引数の各ブランチに対して独自の関数が呼び出され、その関数がブランチのすべての引数を処理し、この関数がargparseモジュールによって自動的に選択され、if / elifがなく、...停止するだけで十分であることを望みます。
例として、 append引数の最初のブランチを使用する最初のトリックを検討してください。
import argparse def create_new_user(args): """ """ # , age = int(args.age) User.add(name=args.username, age=args.age) def parse_args(): """ argparse""" parser = argparse.ArgumentParser(description='User database utility') subparsers = parser.add_subparsers() parser_append = subparsers.add_parser('append', help='Append a new user to database') parser_append.add_argument('username', help='Name of user') parser_append.add_argument('age', help='Age of user') parser_append.set_defaults(func=create_new_user) # return parser.parse_args() def main(): """ , """ args = parse_args() args.func(args)
次に、ユーザーがパラメーターを使用してスクリプトを実行した場合、たとえば:
userdb.py append RootAdminCool 20
プログラムの腸内では、 create_new_user()関数が呼び出され、すべてが実行されます。 各ブランチに独自の関数を構成するため、 メイン()エントリポイントはSpartanショートであることが判明しました。 既にお気づきのように、トリックはset_defaults()メソッドの呼び出しにあります。これにより、定義済みの値で固定パラメーターを設定できます。この場合、値を持つすべてのブランチにfuncパラメーターが必要です。呼び出されるオブジェクトは、1つの引数を取ります。
ちなみに、ユーザーがそのような欲求を持っている場合、スクリプトに入らずにfuncとしてパラメーターを「外側」にスリップすることはできません(少なくとも成功しませんでした)。
これで、userdb.pyの引数の2番目のブランチにある2番目のトリックを考慮する以外に何も残っていません。
userdb.py show <userid>
2番目のトリックの目標は次のように定式化されます: ユーザーが送信するデータを検証するだけでなく、
create_new_user()関数の最初のトリックで、データの有効性を「面倒なチェック」していることに注目してください 。 次に、それらを所属する場所に転送する方法を学習します。
argparseには、各引数typeに設定できるパラメーターがあります。 argsオブジェクトプロパティに書き込まれる値を返す実行可能オブジェクトは、 typeとして設定できます。 タイプを使用する最も簡単な例は次のとおりです。
parser.add_argument(..., type=file) parser.add_argument(..., type=int)
しかし、この方法をもう少し進めます。
import argparse def user_from_db(user_id): """ -, id , . """ # user_id id = int(user_id) return User.select(id=id) # ORM def print_user(args): """ . , args.userid ID, ORM. , ( - !) """ user_obj = args.userid print str(user_obj) def parse_args(): """ argparse""" parser = argparse.ArgumentParser(description='User database utility') # subparsers = parser.add_subparsers() parser_show = subparsers.add_parser('show', help='Show information about user') parser_show.add_argument('userid', type=user_from_db, help='ID of user') parser_show.set_defaults(func=print_user) return parser.parse_args()
main()のエントリポイントは変更されません!
ここで、ユーザーがパラメーターとしてIDを入力することは残酷であると後で理解した場合、たとえばユーザー名に安全に切り替えることができます。 これを行うには、 user_from_db()コードを変更するだけでよく、 print_user()関数は何も認識しません。
typeパラメーターを使用すると、このパラメーターの値として渡される実行可能オブジェクト内で発生する例外がargparse内で処理されるという事実に注意する価値があります。エラー情報は対応する引数でユーザーに送信されます。
ハーフレセプション。
このトリックは本格的なレセプションのタイトルに値するものではありませんでした。これは2番目のレセプションの延長であるためです。 print_user()のドキュメント(私は__doc__について話している)を見ると、 args.useridがinputに送信されていることがわかります 。これは実際にはIDではなく、完全なユーザー情報を持つより複雑なオブジェクトです。 これはコードを混乱させ、コメントを必要としますが、これはいです。 悪の根源は、ユーザーが操作する情報とプログラムが操作する情報の不一致です。
問題を定式化する時が来ました:
パラメーターの名前はユーザーが理解できるようにしたいのですが、同時にこれらのパラメーターを操作するコードは表現力豊かです。
これを行うために、 argparseの位置引数には、ユーザーに表示される引数の名前を指定するmetavarパラメーターがあります( destパラメーターはオプションの引数に適しています)。
次に、2番目の例のコードを変更して問題を解決してみましょう。
def print_user(args): """ """ print str(args.user_dbobj) def parse_args(): """ argparse""" # parser = argparse.ArgumentParser(description='User database utility') subparsers = parser.add_subparsers() parser_show = subparsers.add_parser('show', help='Show information about user') parser_show.add_argument('user_dbobj', type=user_from_db, metavar='userid', help='ID of user') parser_show.set_defaults(func=print_user) return parser.parse_args()
これで、ユーザーにはuseridプロパティが表示され、パラメーターハンドラーはuser_dbobjです。
私の意見では、両方の2.5タスクを解決することが判明しました。 その結果、ユーザーからのデータを処理するコードはメインプログラムコードから分離され、プログラムエントリポイントはブランチで過負荷にならず、ユーザーとプログラムはそれぞれ独自の条件で機能します。
一度にすべてが「風水」である例の作業コードはこちらです。