Thunderargs:使用の練習。 パート1

私は最近、 雷鳴がどのように発明され、書かれたかについての投稿を書きました。 今日は、それをどのように適用できるかについてお話します。

このことは、注釈を使用して関数パラメーターを処理するように設計されていることを思い出させてください。 たとえば、次のように:

OPERATION = {'+': lambda x, y: x+y, '-': lambda x, y: xy, '*': lambda x, y: x*y, '/': lambda x, y: x/y, '^': lambda x, y: pow(x,y)} @Endpoint def calculate(x:Arg(int), y:Arg(int), op:Arg(str, default='+', expander=OPERATION)): return str(op(x,y)) 


チュートリアルでは、一時的な問題ではなく、非常に具体的な問題の解決を試みます。 さて、ここまでです。


今日はすべての例(またはほぼすべて)でFlaskを使用します。 このフレームワークに少しでも精通している人は、フォームから引数を抽出する問題が痛みと屈辱であることをよく知っています。 さて、さらに、最後のトピックで、不必要なトラブルを起こさずにフラスコと一緒にサンダー引数を使用できるようにする記事をすでに書きました。

ところで、 ここにある例のすべてのコードを取り上げることができます 。 Flask-exampleファイルが必要です。

行こう



ステップ0:注釈構文、または魔法の効果を取り除く


注釈構文の詳細については、 こちらをご覧ください

そしてここでは、本当に必要なもの、つまり引数を記述するための構文のみを見ていきます。 これは次のように行われます。

 def foo(a: expression, b: expression): ... 


その後、 __annotations__関数の__annotations__フィールドを介して引数の説明にアクセスできます。

 >>> def foo(a: "bar", b: 5+3): pass >>> foo.__annotations__ {'b': 8, 'a': 'bar'} 


ご覧のとおり、ここには注釈付き変数の名前と計算された式が辞書の値としてあります。 これは、関数の宣言中に計算される任意の式を注釈で突き出すことができることを意味します。 使用するのはこのチップです。 方法を正確に知りたい場合は、この記事の冒頭にリンクが記載されている投稿を読むことを歓迎します。

ステップ0.5:インストール


ある男のリクエストでこのものをPyPIにアップロードしたので、pipで安全にインストールできます。 唯一の修正:マニュアルで触れるいくつかの機能はアルファ版のみであるため、 --preを使用することをお勧めし--pre

 sudo pip install thunderargs --pre 


そして、フラスコを置くことを忘れないでください! 理論的には、雷鳴が機能する必要はありませんが、現在のマニュアルではそれを使用します。

ステップ1:基本型キャスト


thunderargsの最も簡単な使用例は、型キャストです。 Flaskには、このような不快な機能があります。引数を前処理する手段がなく、エンドポイント関数の本体で直接処理する必要があります。

単純なページネーションを書きたいとします。 オフセットと制限の2つのパラメーターがあります。

これに必要なのは、これらの引数が与えられるデータのタイプを示すことです:

 from random import randrange from thunderargs.flask import Flask app = Flask() # Just a random sequence elements = list(map(lambda x: randrange(1000000), range(100))) @app.route('/step1') def step1(offset: Arg(int), limit: Arg(int)): return str(elements[offset:offset+limit]) if __name__ == '__main__': app.run(debug=True) 


以下では、古典的なFlaskではなく、 thunderargs.flaskからインポートするroute関数が置き換えられたバージョンを使用していることに注意してください。

そのため、注釈に型キャストを詰め込むことができ、今では次のような愚かな操作を行う必要がなくなりました。

  offset = int(request.args.get('offset')) limit = int(request.args.get('limit')) 


関数の本体内。 もう悪くない。 しかし問題は、未処理の確率が非常に多く残っていることです。 誰かが負の制限値を入力すると推測した場合はどうなりますか? 誰かがまったく何も言わない場合はどうなりますか? 誰かが数字を入力しないとどうなりますか? 心配しないでください、これらの例外に対処するツールがあります。それらを考慮します。

ステップ2:デフォルト値


私たちの現在の例が私たちに合っている限り、私たちは新しいものを思い付かず、単にそれを補完します。

私の意見では、デフォルト値は非常に直感的に設定されています。

 @app.route('/step2') def step2(offset: Arg(int, default=0), limit: Arg(int, default=20)): return str(elements[offset:offset+limit]) 


これについてはまだ詳しく説明しません。 おそらく、1つの事実を除いて、デフォルト値は指定されたクラスのインスタンスでなければなりません。 たとえば、この場合、 [0,2,5]デフォルト値として[0,2,5][0,2,5]

ステップ3:必須の引数


 @app.route('/step3') def step3(username: Arg(required=True)): return "Hello, {}!".format(username) 


コードですべてが明確になったと思います。 ただし、明確にする必要があります。デフォルトと必須の両方を使用することはできません。 そのような試みはエラーを発生させます。 これは、発生する可能性のある論理エラーに対する一種の保護であり、発見するのは非常に困難です。

また、必要な引数をサーバーにthunderargs.errors.ArgumentRequiredないと、 thunderargs.errors.ArgumentRequiredが取得されます。

ステップ4:複数の引数


ここのすべても非常に明白です。 また、 map objectは、リストではなくパラメーターとして関数に送られる可能性がありmap object

 @app.route('/step4') def step4(username: Arg(required=True, multiple=True)): return "Hello, {}!".format(" and ".join(", ".join(username).rsplit(', ', 1))) 


誰かがすでに忘れている場合、ユーザーが1つの名前に多くの値を指定すると、そのような引数がスローされます。 リクエストの例:

 ?username=John&username=Adam&username=Lucas 


もちろん、この場合のデフォルト値はリストの形式で提示する必要があり、このリストの各引数はすべての条件を満たす必要があります。

ステップ5:バリデーター


思い出させると約束したページネーターを使用して、例に戻りましょう。

 from thunderargs.validfarm import val_gt, val_lt @app.route('/step5') def step5(offset: Arg(int, default=0, validators=[val_gt(-1), val_lt(len(elements))]), limit: Arg(int, default=20, validators=[val_lt(21)])): return str(elements[offset:offset+limit]) 


バリデータは、validfarmと呼ばれる工場ファームで作成されます。 現在、len_gt、val_neqなどの最も基本的なオプションのみがありますが、将来的にはリストが更新されると思います。

しかし、誰も私たちをあなたのバリデータにすることを気にしません。 値を受け取り、この値が値を満たしているかどうかに関係なくブール値の応答を返す単純な関数でなければなりません。

 def step5(offset: Arg(int, default=0, validators=[lambda x: x >= 0 and x < len(elements)]), limit: Arg(int, default=20, validators=[val_lt(21)])): ... 


またはこのように:

 def less_than_21(x): return x < 21 @app.route('/step5_5') def step5_5(offset: Arg(int, default=0, validators=[lambda x: x >= 0 and x < len(elements)]), limit: Arg(int, default=20, validators=[less_than_21])): ... 


一般に、呼び出すことができるものは、引数が1つだけ渡されたときに機能し、ブール値の答えを返すものであれば、バリデーターに役立ちます。

ステップ6:引数を展開する


多くの場合、どの引数を使用する必要があるかを示すキーをユーザーから取得する必要があります。 この場合、スキャンが必要です。

実証するために、冒頭ですでに述べた機能を再度説明します。 今回はすべてがここで明確になると思います。

 OPERATION = {'+': lambda x, y: x+y, '-': lambda x, y: xy, '*': lambda x, y: x*y, '^': lambda x, y: pow(x,y)} @app.route('/step6') def step6(x:Arg(int), y:Arg(int), op:Arg(str, default='+', expander=OPERATION)): return str(op(x,y)) 


ご覧のとおり、 opはユーザーから受け取ったキーによって取り出されます。

expanderは、ディクショナリまたはオブジェクトと呼ばれます。 そこで、たとえば、指定されたキーを使用してデータベースから目的のオブジェクトを抽出する関数を突き出すことができます。

本日は機能的なレビューで終わります。 手動でコメントを2、3だけ付けたいと思います。

追加コメント



Python 2または化石のオプション


原則として、このソフトウェアを2番目のpythonに逆転送することを不可能にするようなものは使用しませんでした。 文字列のフォーマットを置き換える必要があります。 そして、おそらく、それがすべてです。 注釈をエミュレートするために、 thunderargs.endpointモジュールにはannotateという単純なデコレーターがあります。 つまり、次のように使用します。

 @annotate(username=Arg()) def foo(username): ... 


理論的には動作するはずですが、実際にはテストされていません。

フラスコの小さなノート


この記事では、GETメソッドのみを検討しましたが、これは他のメソッドがサポートされていないという意味ではありません。 気にしないように選んだ。 しかし、微妙な点が1つあります。1つの目的関数に対して複数のメソッドが必要ではないため、各関数が1つのメソッドのみを担当できるようになったと思います。 私の意見では、これによりコードが読みやすくなります。

突然ネイティブフラスコルーティングが本当に必要な場合は、 app.froute使用しapp.froute 。 ただし、注釈トークンがそこで機能しないことを忘れないでください。

理論的には、他のフラスコモジュールとの相互作用は中断されません。 しかし、練習が表示されます。

アノテーションのパス変数とパラメーターの両方を同時に使用できます。 パラメータのいずれかが両方ではない場合、それらは互いに競合しません。

非フラスコに関する小さなメモ


雷鳴はフラスコがなくても正常に機能することを覚えておく必要があります。 これを行うには、関数でthunderargs.EndpointEndpointデコレータを使用する必要があります。

ただし、乱用しないでください。 実際、ハードコアな引数処理はコントローラーでのみ必要です。

Argから独自の子孫を簡単に作成できることを忘れないでください。 IntArg、StringArg、BoolArgなど。 このような最適化により、関数宣言の文字数が大幅に削減され、コードが読みやすくなります。

私たちはそれに取り組んでいます。


ほとんどのコードは酔っていました。 一部は非常に眠い状態です。 コードには最適化が必要ですが、すでに何らかの形で機能しています。 開発と実行は進行中です。そのため、突然支援することに決めた場合は参加してください。 すべてのヘルプ、特にテストに行きます。 私は、チュクチについての古い冗談のように、読者ではなく作家です。 これまで。

ここ、コメント、またはここに提案、要望、批判を書くことができます

ちなみに、残念なことに、5月の英語のメモはテキストを翻訳するメモが書かれているので、誰かがこれをやったら、とても感謝しています:)

第二部は少し難解ですが、私が望むように、非常に興味深いものです。 しかし、残念ながら、それはすぐにはありません。

Source: https://habr.com/ru/post/J224705/


All Articles