Pythonのドキュメントには以下が含まれていますHTTPサーバーの作成例:
def run(server_class=HTTPServer, handler_class=BaseHTTPRequestHandler):
server_address = ('', 8000)
httpd = server_class(server_address, handler_class)
httpd.serve_forever()
RequestHandler
にクラスが提供され、Server
ハンドラーのインスタンス化が自動的に処理されます。
リクエスト ハンドラーの作成時にカスタム パラメーターを渡したいとします。どうすればよいでしょうか。また、どうすればよいでしょうか。
もっと具体的に言うと、コマンド ラインからパラメータを渡したいのですが、sys.argv
リクエスト ハンドラ クラス内でアクセスする必要があるのは不必要に面倒に思えます。
これはクラスの一部をオーバーライドすることで可能になるように思えますServer
が、よりシンプルで優れた解決策を見落としているような気がします。
ベストアンサー1
私は自分のコードで「部分適用」を使ってこれを解決しました。Pythonの標準ライブラリはこれを次のように提供しています。functools.partial
。
例は Python 3 を使用して記述されていますが、部分適用は Python 2 でも同じように動作します。
from functools import partial
from http.server import HTTPServer, BaseHTTPRequestHandler
class ExampleHandler(BaseHTTPRequestHandler):
def __init__(self, foo, bar, qux, *args, **kwargs):
self.foo = foo
self.bar = bar
self.qux = qux
# BaseHTTPRequestHandler calls do_GET **inside** __init__ !!!
# So we have to call super().__init__ after setting attributes.
super().__init__(*args, **kwargs)
def do_HEAD(self):
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.end_headers()
def do_GET(self):
self.do_HEAD()
self.wfile.write('{!r} {!r} {!r}\n'
.format(self.foo, self.bar, self.qux)
.encode('utf8'))
# We "partially apply" the first three arguments to the ExampleHandler
handler = partial(ExampleHandler, sys.argv[1], sys.argv[2], sys.argv[3])
# .. then pass it to HTTPHandler as normal:
server = HTTPServer(('', 8000), handler)
server.serve_forever()
これはクラス ファクトリーと非常に似ていますが、私の意見では、いくつかの微妙な利点があります。
partial
オブジェクトは、ファクトリ関数によって定義され返されるネストされたクラスよりも、その内部にあるものをイントロスペクトする方がはるかに簡単です。partial
現代の Python ではオブジェクトはシリアル化できますが、ファクトリ関数内のネストされたクラス定義はシリアル化できません (少なくとも、それを可能にするためにクラスにメソッドpickle
をコーディングしない限りは)。__reduce__
partial
私の限られた経験では、Pythonの通常のクラス定義に明示的に引数を「事前に追加する」ことは、より簡単に(認知負荷が少ない)読む、理解する、正確性を確認するラッピング関数のパラメータが内部のどこかに埋め込まれたネストされたクラス定義よりも優れています。
唯一の欠点は、多くの人が馴染みがないことですpartial
が、私の経験では、みんなになるとにかく、ここのように、時には予期せず、多くの場所で簡単で構成可能なソリューションとして現れる方法があるpartial
ため、よく知られています。partial