name: www.py-htdocs class: title, smokescreen, shelf, no-footer # <small>www.pyソースコード解説</small> <div class=footnote> <small><small><small> </small></small></small> </div> --- name: www.py-htdocs class: title, smokescreen, shelf, no-footer # <small>www.pyソースコード解説<br>Part.1 WWWサーバの基本部分</small> <div class=footnote> <small><small><small> (脚注) 配布したwww.pyはWWWサーバの基本機能つまり 「HTTPリクエストに応えてコンテンツ(ファイル)を返す」部分だけを実装しています。 それ以外にも課題で使う関数が定義されていますが、その中身がありません。 みなさんには、そこを実装してもらいます:-) </small></small></small> </div> --- class: img-right,compact # <small>www.py全体像(基本的なWWWサーバ)</small> <div class=footnote> <small><small> (脚注) T.B.Leeが最初に考えた動作(HTTP version 1.0以前)です。 CERN(ヨーロッパで素粒子実験をする研究所)の中で、これがしたい -> 「自分の論文を自分のホームページ(自分のPCで動かしているWWWサーバ)に置いたから、 みんな見てね!」 <br> ちなみにCGIはHTTP/1.0です。 これは1993年のWWW流行後、いろいろな拡張が追加された規格になります (CGIは次回を予定) </small></small> </div> ![height400px](../../review/images/www-internal-htdocs.png) <small> - **基本**的なWWWサーバの動作とは? - **サーバはファイルを探してブラウザへ返す** - **GETメソッドのみ** - 右図は以前も説明に使った図(再掲) - `http://api.fm.org/upload/`を表示する例 - 顧客 = みなさんの**PC上で実行されているブラウザ**アプリ(Chromeなど) - 給仕 = **東京**にあるサーバ上の**WWWサーバ** - つまり実体は**1000km**ほど離れている - 図の(1)(3)は**1000km**の通信 </small> --- class: compact # <small>www.pyのWWWサーバ部分の解説</small> <small> ``` 0001 # 今回の説明に無関係なimportと余分なコメントを省略。説明の都合で行番号を追加 0002 import http.server 0003 import socketserver 0004 0005 # Configurations 0006 HTTP_HOST = "0.0.0.0" 0007 HTTP_PORT = 8080 0008 HTDOCS_DIR = "/var/www/html" 0009 0010 class httpHandler(http.server.SimpleHTTPRequestHandler): 0011 def __init__(self, *args, **kwargs): 0012 super().__init__(*args, directory=HTDOCS_DIR, **kwargs) 0013 0014 if __name__ == "__main__": 0015 with socketserver.TCPServer((HTTP_HOST, HTTP_PORT), httpHandler) as httpd: 0016 print("(debug) serving at port", HTTP_PORT, file=sys.stderr) 0017 httpd.serve_forever() ``` --- name: www.py class: compact # <small>www.pyのWWWサーバ部分: モジュール群</small> <small> ``` 0002 import http.server 0003 import socketserver ``` - WWWサーバに必要なモジュールをimportします。 - 0003行目の [socketserver](https://docs.python.org/ja/3/library/socketserver.html) は汎用のネットワークサーバを作成するモジュールです。 トランスポート層の代表的なプロトコルTCPもしくはUDPのサーバを作成できます - [socketserver](https://docs.python.org/ja/3/library/socketserver.html)は、 アプリケーションプロトコルの詳細は理解していません - HTTPの処理をするには、 0002行目の [http.server](https://docs.python.org/ja/3/library/http.server.html) モジュールが必要です </small> --- class: img-right,compact # <small>WWWサーバの基本動作(1)</small> <div class=footnote> <small><small><small> (脚注) (1)(2)(3)すべて、 ブラックボックスです。 実体は、スーパークラスの奥深くにあり、 www.pyを読んでも直接は読み取れません </small></small></small> </div> ![height400px](../../review/images/www-internal-htdocs.png) <small> - (1) ブラウザからwww.pyへTCPコネクションを張ります。 それをwww.pyが受けつけます - (2) www.pyの動作の実体 - (3) www.pyから、(1)で張ったTCPコネクションを使い、 コンテンツ((2)で見つけたファイルの中身)をブラウザへ送り返します - (4) ここはブラウザの動作なのでサーバは無関係 </small> --- class: img-right,compact # <small>WWWサーバの基本動作(2): telnet ... 80の復習</small> <div class=footnote> <small><small> (脚注) コンピュータネットワークで行った演習のおさらいと、 前ページのPythonモジュールとの対応関係です </small></small> </div> ![height400px](../../review/images/www-internal-htdocs.png) <small> ``` $ telnet api.fml.org 80 Trying 59.106.191.18... Connected to mikros.fml.org. Escape character is '^]'. GET /upload HTTP/1.0 ``` - 図の(1)は2段階に分かれています 1. telnetコマンドでTCPコネクションを張る 2. `GET 〜`コマンドを送る(HTTPプロトコル) - **`socketserver`モジュール**はトランスポート層だけを担当しています。 **「telnetコマンドでTCPコネクションを張る」部分**に相当します。 TCPコネクションまでを担当していて、 個別のアプリケーションプロトコルは理解できません - そこで**`http.server`**が必要です。 このモジュールが**HTTPプロトコルの処理**を担当します </small> --- name: www.py-config class: compact # <small>www.pyのWWWサーバ部分: 設定変数 - リリースのお作法 -</small> <div class=footnote> <small><small> <small><small> (脚注1) 個人利用の短いプログラムの場合、 このように一ヶ所にグローバル変数群を定義しておき、 適宜、変更して使えば十分でしょう。 なお、フリーソフトウエアとしてリリースする気があるなら、 もっときちんと書くべきです。 ユーザが期待するリリースの仕方というものがあります。 いわゆるリリースエンジニアリングですな、それを勉強しましょう (脱線なので省略) <br> (脚注2) HTTP_HOSTの<B>全住所</B>のくだりですが、 これは一つのPCに住所(IPアドレス)が複数ある場合があるからです。 詳細は、 コンピュータネットワーク第4回「インターネット層(1)」を参照 </small></small> </small></small> </div class=footnote> <small><small> ``` 0006 HTTP_HOST = "0.0.0.0" 0007 HTTP_PORT = 8080 0008 HTDOCS_DIR = "/var/www/html" ``` - 設定変更可能な変数群です。 - `HTTP_HOST` ... リクエストを待ちかまえる住所(IPアドレス)。 0.0.0.0は全住所という特別な書き方 - `HTTP_PORT` ... HTTPを受け付けるには80に変更。 本番前に間違って起動された時の対策としてデフォルト値は8080 - `HTDOCS_DIR` ... コンテンツを探す際のトップディレクトリ。 `/var/www/html`はLinuxでよくみかける初期値。 検索例: - `http://api.fml.org/`、 つまり/というコンテンツは`/var/www/html/`直下のindex.htmlファイルを意味します <br> **(ファイル名を指定しない場合、ファイル名index.htmlを補完しています)** - `http://api.fml.org/janken/`の場合、 `HTDOCS_DIR/janken/`フォルダの`index.html` つまり `/var/www/html/janken/index.html` を探します。 なお、 `http://api.fml.org/janken/kekka.html` というファイル名つきのURIでは `/var/www/html/janken/kekka.html`を検索します </small></small> --- class: compact # <small>www.pyのWWWサーバ部分: 汎用TCPサーバ(1)</small> <div class=footnote> <small> (脚注)<A HREF="https://docs.python.org/ja/3/reference/compound_stmts.html#the-with-statement">with</A>は、コンテキストの終了時にファイルをクローズする構文 </small> </div class=footnote> <small> ``` 0014 if __name__ == "__main__": 0015 with socketserver.TCPServer((HTTP_HOST, HTTP_PORT), httpHandler) as httpd: 0016 print("(debug) serving at port", HTTP_PORT, file=sys.stderr) 0017 httpd.serve_forever() ``` - 0014行目は 「このスクリプト(**自分自身)が起動された場合**」 を意味する**定番の条件文**です。 このスクリプトでは、ほとんど意味がありませんが、 **デバッグコードやテストコードは、 ライブラリとしてimportされた場合は実行されないように、 このように書くもの**です - 0015と0017行目がペアです。 0015行目で、 Pythonの`with ... as `構文で**変数名httpdとして汎用TCPサーバを起動**します。 0015行目が成功した場合にのみ0016-0017行目が実行されます。 - 0016行目はデバッグコードです。 起動時に開いたポート番号をターミナル上に表示します - 0015行目の`TCPServer()`の引数はサーバの必須パラメータになります。 メソッドが`TCPServer()`なので(当たり前ですが)対応するプロトコルはTCPだけです - `HTTP_HOST` ... [設定変数](#www.py-config)を参照 - `HTTP_PORT` ... [設定変数](#www.py-config)を参照 - `httpHandler` ... 新しいリクエストを受け取った際に呼び出す**クラス**として`httpHandler`を指定 </small> --- class: compact # <small>www.pyのWWWサーバ部分: 汎用TCPサーバ(2)</small> <small> ``` 0014 if __name__ == "__main__": 0015 with socketserver.TCPServer((HTTP_HOST, HTTP_PORT), httpHandler) as httpd: 0016 print("(debug) serving at port", HTTP_PORT, file=sys.stderr) 0017 httpd.serve_forever() ``` - 0017行目の `httpd.serve_forever()` つまり`socketserver`モジュールの`serve_forever()`メソッドを呼び出し、 無限ループに入っています。 このループでは、 新しいTCPのリクエストを受け取るたびに、 登録した**クラス**を呼び出し、 次のリクエストを待つという繰り返し処理(無限ループ)をしています - 詳細はPython3公式ドキュメント(日本語訳) - https://docs.python.org/ja/3/library/socketserver.html <br> 注: `serve_forever()`は`socketserver`クラスの関数です。 `with`文で`httpd`変数を定義したため、 `httpd.serve_forever()`と書いていることに注意しましょう </small> --- class: compact # <small>TCPコネクションを受け取る時の動作は???</small> <small> - TCPコネクションを受け取る時の動作はソースコードから**読み取れません**。 いわゆる高級言語では、 そういうOSに近いレベルのことは**ブラックボックス**なので、 詳細が分からなくてもコードが書けます - 下のコードがC言語の例です。 これがPythonの実行環境(runtime)かモジュールの奥深くにあるコードになります。 もう少し詳細な解説は、 コンピュータネットワークの[「WWW(通常版)」の付録](/slides/network/tcpip/app_www/#www-src-examples)を参照 ``` struct sockaddr_in sa; // サーバのIPアドレスとポート番号を格納する構造体 int listen_socket, fd; // ソケット listen_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_IP); setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &sopt, sizeof(sopt)); bind(listen_socket, (struct sockaddr *)&sa, sizeof(sa)); listen(listen_socket, SOMAXCONN); for(;;) { fd = accept(listen_socket, (struct sockaddr *)&src_addr, &len);// リクエストを受け取る、それまで待つ(停止) recv(fd, buf, buflen,....); // データを受け取る send(fd, buf, buflen,....); // データを送信する } ``` </small> --- class: img-right,compact # <small>WWWサーバの基本動作: 0015と0017行の解説 </small> <div class=footnote> <small><small> (脚注) インスタンス = 新しい変数(C言語ならstruct)の生成です。 もちろんstructの要素の中身はインスタンスごとに異なります </small></small> </div> ![height400px](../../review/images/www-internal-htdocs.png) <small> - 図(1)でTCPコネクションを受け止める部分はコードの奥深くで勝手に実行されます。 そういうものだと、あきらめてください - 新しいHTTPリクエスト(80/tcpのTCPコネクション)を受け付けるたびに、 **そのHTTPリクエストに対応するオブジェクト(httpHandler クラスのインスタンス)が生成されます** - コンストラクタはHTTPリクエストを解析し、 その結果をインスタンス内の各種変数(例: self.path)に設定してくれるので、 それを利用できます </small> --- name: www.py-httpHandler class: compact # <small>www.pyのWWWサーバ部分: httpHandlerクラス</small> <div class=footnote> <small><small> (脚注) `(self, *args, **kwargs):` ... Python独特の表記法で、 関数の引数としてリストとdictを渡せます。 詳細はPythonの本を参照 <br> ちなみにポインタとポインタへのポインタではありません;-) ヨーロッパの矜持で?わざとC言語系に逆らう文法が使いにくいPython </small></small> </div class=footnote> <small> ``` 0010 class httpHandler(http.server.SimpleHTTPRequestHandler): 0011 def __init__(self, *args, **kwargs): 0012 super().__init__(*args, directory=HTDOCS_DIR, **kwargs) ``` - 0015行目の`socketserver.TCPServer()`メソッドで登録したクラスです。 ユーザがWWWサーバの挙動をカスタマイズするには、 このように自分のクラスを作成する必要があります - 0010行目はクラスの宣言です。`()`の中には継承関係を定義します。 `httpHandler`クラスの親クラス(スーパークラス)は `http.server.SimpleHTTPRequestHandler`クラスです - 0011-0012行目では`httpHandler`クラスのコンストラクタ(`__init__`メソッド)を定義し、 (0012行目で)すべて親クラス(スーパークラス)に丸投げしています:-) - 0012行目が「すべて親クラス(スーパークラス)に丸投げ」という意味です。 ただし、WWWサーバの機能自体は丸投げですが、 **引数で`directory=HTDOCS_DIR`を追加し、 コンテンツを探す場所を指定(カスタマイズ)**するようにしています。 これによりURIで指定されたコンテンツはHTDOCS_DIR以下を検索します </small> --- class: img-right,compact # <small>WWWサーバの基本動作: 0012行 </small> <div class=footnote> <small><small> (脚注) www.pyのソースコードを読んでも、この動作は読み取れません。 気になる人は、スーパークラスのソースコードを読もう:-) </small></small> </div> ![height400px](../../review/images/www-internal-htdocs.png) <small> ``` super().__init__(*args, directory=HTDOCS_DIR, **kwargs) ``` - 0012行目は図の(2)の部分にあたります - 図(2)の「ファイルを探しにいく」際に、 `HTDOCS_DIR`以下を探せという指示が `directory=HTDOCS_DIR`の部分です - この探す場所の指示以外は、 スーパークラスの動作そのものです。 古典的なWWWサーバの「指定されたパスのファイルを探して返す」動作をします </small> --- class: img-right,compact # <small> do_GET() </small> <div class=footnote> <small><small> (脚注1) 最終課題で、かなりマニアックな改造をする際には、このあたりの知識が必要になるはずです <br> (脚注2) www.pyのソースコードを読んでも、この動作は読み取れません。 気になる人は、スーパークラスのソースコードを読もう:-) </small></small> </div> ![height400px](../../review/images/www-internal-htdocs.png) <small> - WWWの基本動作とは **「HTTPのGETメソッドでコンテンツ(ファイル)を取得する」** ことです - メソッドとはHTTPの動作モードのようなものだと思ってください - (Python3の場合)各HTTPリクエストに応じて、 (作成されたインスタンスは)対応するメソッド名の関数を呼び出します - スーパークラスを見ると`do_メソッド名()`という関数が定義されています - 今回は**`do_GET()`が呼び出されます** - 図の(2)でファイルを探索し、中身を読み出す実作業は、この関数が行います - httpHandlerクラスには`do_GET()`ナシでOKです。 **未定義の場合は、スーパークラスの`do_GET()`**が呼ばれます </small> --- name: www.py-janken-cgi class: title, smokescreen, shelf, no-footer # <small>www.pyソースコード解説<br>Part.2 CGI (じゃんけんAPI)</small> --- class: img-right,compact # <small> CGI の動作 </small> <div class=footnote> <small><small> (脚注1) 一般に、 図の給仕=WWWサーバはapacheやnginxというソフトウエアで、 図(2)のコック=下請けは、サーバとは別の(自作する)ソフトウエアです。 下請けを呼び出す = 新しい下請けプログラムを起動して仕事をまかせること(プロセスの生成)を意味します <br> (脚注2) CGIのお約束が守れれば、 <B>下請けを書くプログラミング言語は何でもOK</B>です。 <B>WWW初期はPerlが大人気</B>、今は何でもアリ? </small></small> </div> ![height400px](/slides/isd/review/images/www-internal-cgi-janken.png) <small> - CGIの場合、 WWWサーバの動作が(Part 1.の)基本動作と異なるのは図(2)の部分だけです - 詳細は[HTMLとCGIのしくみ](/slides/isd/review/html-form/#www-internal-cgi-example-janken)を参照 - 図(2)の部分とは? - 難しい仕事を担当する下請けプログラム - www.pyではhttpHandlerクラス内に作りこむ </small> --- class: compact # <small> (先へ進む前に) HTTP メソッドと Web API (1) GET </small> <div class=footnote> <small><small> </small></small> </div> <small> - HTTPメソッド(動作モードのようなもの)が、いくつか定義されています - デフォルトはGET - よくCGIで使うのがPOSTです。なおPOSTと似たメソッドとしてPUTがあります - GETは「指定したファイルをGETしたい」という意味になります。文字どおりですよね? - URIにはファイルの場所(file path)が含まれているため、 WWWサーバは、その場所を検索して中身(コンテンツ)を返します。 ファイルが存在しない場合はエラーが返ります(エラーコードは404) ``` http://サーバ"/ファイルの場所の指定" [例] http://api.fml.org/upload/ ``` - HTDOCS_DIR(コンテンツの最上位フォルダ)以下にある "/upload/index.html"というファイルを返す </small> --- class: compact # <small> (先へ進む前に) HTTP メソッドと Web API (2) POST</small> <div class=footnote> <small><small> (脚注) POSTやPUTは英語の語感のとおり、ファイルを押し込むようなイメージです。 本来は、 PUTメソッドでサーバにファイルをアップロードしたり、 POSTでファイルの更新をしたりすることが想定されていたようですが、 たいてい、そういう使い方はしません。 <B>ファイルが無い場合は作成もありうる</B>想定です (でも、<B>そんな危ないことをサーバ管理者はさせたくありません</B>) </small></small> </div> <small> ``` [例] FORMタグのACTIONに指定するURIの例 http://api.fml.org/cgi-bin/upload.cgi (昔風、下請けプログラムの場所) http://api.fml.org/api/upload (今風、いわゆるWeb APIサーバ) ``` - 一方POSTやPUTも同じようなURIを使いますが、 いまどきは必ずしもファイルを意味しません - 昔風の書き方では、 下請けプログラムのパス`/cgi-bin/upload.cgi`をFORMタグのACTIONに書きました。 これは**GETと同様のファイルの場所(パス)**の意味です。 HTTPリクエストを受けるたびに、 WWWサーバは`upload.cgi`プログラムを起動して仕事をまかせます - 今風なのは`/api/upload`のほうです。 ファイルの場所という意味はなくて、 (目的地を意味する)**単なる条件として使う文字列**です (そういうわけで、ウエブ屋さんは、これをルーティングと呼んでいます) ``` [例] do_POST()内で条件文として使う if self.path == "/api/upload": self.upload() ``` </small> --- class: compact # <small> HTML FORM文 (答え合わせ)</small> <small> ``` <FORM METHOD="POST" ACTION="http://api.fml.org/api/janken/v1"> <INPUT TYPE="text" NAME="jibun"> <INPUT TYPE="submit"> </FORM> ``` - ジャンケンの場合、使うAPIサーバは http://api.fml.org/api/janken/v1 - ジャンケンの手の入力欄では`NAME="jibun"`と設定する(APIサーバと仕様を合わせる) - FORMタグのMETHOD, INPUTタグのTYPEは以下のとおり | タグ名 | 属性 | 例 | 説明 | 備考 | |-------- |--------- |---------------------------------- |---------------------------------- |------------------------------ | | FORM | ACTION | http://api.fml.org/api/upload | URI | | | | METHOD | POST | HTTPメソッドの指定 | HTTPの細かな動作モードの指定 | | INPUT | TYPE | submit | 送信ボタン | | | | TYPE | text | 文字の入力欄 | | | | NAME | jibun | 変数名 | | </small> --- class: compact # <small> じゃんけんAPIサーバの概要 (おさらい)</small> <div class=footnote> <small><small> </small></small> </div> <small> - 下請けのプログラムは次のステップから構成されます 1. 入力 ... (ワークシートで解説済み) - CGIの約束事にしたがいブラウザからデータを受け取ります - 解説どおりにコピー&ペーストしてください 1. 処理 ... **ここが課題です。作りこんでください** - でも、じゃんけん自体は(文法がPythonになるだけで)C言語のときと同じです 1. 出力 ... (テンプレートに解説) - ブラウザに返す部分はテンプレートに作り込んであります - 変数dataに代入するだけです。あとはよろしく動きます - どういう理屈なのか?はテキストに解説があるとおりです - 資料: - [テキスト] **https://sysbuild-entrance.fml.org/ja/300_web-api/350_janken-api/** </small> --- class: img-right,compact # <small> Python3 の各クラス間の関係/動作 </small> <div class=footnote> <small><small> テキスト: <A HREF="https://sysbuild-entrance.fml.org/ja/300_web-api/350_janken-api/"> https://sysbuild-entrance.fml.org/ja/300_web-api/350_janken-api/ </A> </small></small> </div> ![](../../review/images/www.py-internal-janken.png) <small> - テキストにある図を書き直したもの - Part 1で話したように、 Python3のような高級言語では 「どのようにHTTPリクエストを受けるか?」 がソースコードを読んでも読み取れません - (読み取れませんが) いちおう右図のような呼び出し順になります - HTML(FORM文)でPOSTを指定しているので、 `do_POST()`メソッドが呼び出されます - `do_POST()`の中でルーティングをします - Web業界のルーティングとは、 **`self.path`変数をみて、 呼び出す先(関数)を切り替える**ことです - (必須)課題では次の2ヶ所を何とかすればOK - `do_POST()`中の条件文 - 呼び出す関数の改造 or 新規作成 </small> --- class: col-2,compact # <small> do_POST() </small> <div class=footnote> <small><small> テキスト: <A HREF="https://sysbuild-entrance.fml.org/ja/300_web-api/350_janken-api/353_do_post/" > https://sysbuild-entrance.fml.org/ja/300_web-api/350_janken-api/353_do_post/ </A> </small></small> </div> <small> ``` def do_POST(self): self._set_headers() data = {} if self.path == "/api/janken/v1": data = self.janken() message = json.dumps(data, ensure_ascii=False) self.wfile.write(bytes(message, "utf8")) ``` <wbr> - テンプレに次の2行を追加しただけです ``` if self.path == "/api/janken/v1": data = self.janken() ``` いわゆるルーティング。 `janken()`メソッドからのdict型の返り値を変数`data`に代入します。 <br> 変数`data`をブラウザに返す部分(最後の2行)はテンプレに最初から仕込まれているので、 そのまま使ってください。 最後の2行がどういう理屈なのか?はテキストを参照 </small> --- class: compact # <small> janken() </small> <div class=footnote> <small><small> テキスト: <A HREF="https://sysbuild-entrance.fml.org/ja/300_web-api/350_janken-api/355_janken/" > https://sysbuild-entrance.fml.org/ja/300_web-api/350_janken-api/355_janken/ </A> </small></small> </div> <small> <small> ``` def janken(self): jibun = self.jibun() aite = self.aite() kekka = (3 + jibun - aite) % 3 return { "jibun": jibun, "aite": aite, "kekka": kekka } ``` </small> - オブジェクト指向(OOP)風に書き直されていますが、 ようするにジャンケンのPython3版です。 ほぼC言語のジャンケンと同じですよね? - 変数`jibun`と`aite`のあたりはOOP風 - 変数kekkaに代入する行はC言語そっくり - `return`文のところでdict型変数を作っています </small> --- class: compact # <small> aite() </small> <div class=footnote> <small><small> テキスト: <A HREF="https://sysbuild-entrance.fml.org/ja/300_web-api/350_janken-api/356_aite/" > https://sysbuild-entrance.fml.org/ja/300_web-api/350_janken-api/356_aite/ </A> </small></small> </div> <small> ``` import random def aite(self): return random.randrange(0,3) ``` - ここが一番簡単で、 randomモジュールの使い方を発見できれば1行で完結です (Google先生に聞けばOK) - randomモジュールを使う場合は、もちろん`import random`が必要です - 解説 `random.randrange(start, stop, step)` - **start以上stop未満の整数をランダムに返し**ます。 例: `randrange(0,3)`の場合0 1 2をランダムに返す - 引数のstepはオプションなので、なくてもOKです。 stepの指定が無い場合は、デフォルト値1 <br> (step=2とすれば一つおきです。 例: `randrange(0,5,2)`は 0 2 4 を返す) - 公式マニュアル - https://docs.python.org/ja/3/library/random.html </small> --- class: col-2,compact # <small> jibun() </small> <div class=footnote> <small><small> テキスト: <A HREF="https://sysbuild-entrance.fml.org/ja/300_web-api/350_janken-api/357_jibun/" > https://sysbuild-entrance.fml.org/ja/300_web-api/350_janken-api/357_jibun/ </A> <br> (脚注) 多くの場面で<B>他のプログラムやサーバとのやりとりは文字列</B>になります。 文字列のままでは扱いづらいので、 <B>プログラミング言語の内部表現に変換</B>が必要です。 C言語の時は入力にscanfを使っています。 scanfにフォーマット指定があるのは、この変換のため </small></small> </div> <div class=footnote> <small><small> </small></small> </div> <small> ``` def jibun(self): form = cgi.FieldStorage( fp=self.rfile, headers=self.headers, environ={'REQUEST_METHOD': 'POST'} ) value = form.getvalue("jibun") jibun = int(value) return jibun ``` - 変数formを作る部分は(WWWサーバを自作しているため)**www.py特有**のコードになります。 ちなみに変数formはFieldStorageクラスのインスタンスです。 詳細はテキストを参照 - formのgetvalueメソッドを使えば、 `<INPUT TYPE=text NAME=jibun>`でユーザが入力した値を取り出せます。 ただし**文字列が返る**ことに注意 - `int(value)`は整数に型変換をしています。 このメソッドの仕様に「整数で返す」とあるからです。 `jibun()`は整数値の`jibun`を返します - 公式マニュアル - https://docs.python.org/ja/3/library/cgi.html </small>