Python文档包含创建HTTP服务器的示例:
def run(server_class=HTTPServer, handler_class=BaseHTTPRequestHandler):
server_address = ('', 8000)
httpd = server_class(server_address, handler_class)
httpd.serve_forever()
Run Code Online (Sandbox Code Playgroud)
RequestHandler提供了一个类Server,然后负责自动实例化处理程序.
假设我想在创建请求处理程序时将自定义参数传递给它.我该怎么做?
更具体地说,我想从命令行传入参数,并且必须访问sys.argv请求处理程序类内部似乎不必要地笨拙.
看起来这应该是可以通过覆盖Server类的部分,但我觉得我忽略了一个更简单和更好的解决方案.
Tho*_*zco 23
使用班级工厂:
def MakeHandlerClassFromArgv(init_args):
class CustomHandler(BaseHTTPRequestHandler):
def __init__(self, *args, **kwargs):
super(CustomHandler, self).__init__(*args, **kwargs)
do_stuff_with(self, init_args)
return CustomHandler
if __name__ == "__main__":
server_address = ('', 8000)
HandlerClass = MakeHandlerClassFromArgv(sys.argv)
httpd = HTTPServer(server_address, HandlerClass)
httpd.serve_forever()
Run Code Online (Sandbox Code Playgroud)
mtr*_*eur 11
我在代码中使用"部分应用程序"解决了这个问题.
示例使用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()
Run Code Online (Sandbox Code Playgroud)
这与一个类工厂非常相似,但在我看来它有一些微妙的优点:
partial 对象内部的内容比工厂函数定义和返回的嵌套类更容易内省.partial对象可以pickle在现代Python中序列化,而工厂函数中的嵌套类定义则不能.partial其他Pythonic和普通类定义明确地"预先附加" 比使用包装函数的参数嵌套的类定义更容易(更少的认知负荷)来读取,理解和验证正确性在它里面的某个地方.唯一真正的缺点是,很多人不熟悉partial-但在我的经验是为了更好的每一个人来变得熟悉partial,无论如何,因为partial有弹出在很多地方容易和组合的解决方案,有时会出现异常,喜欢这里的一种方式.
在撰写本文时,这里的所有答案基本上都坚持(非常尴尬)的意图,即socketserver模块的作者似乎让传入的处理程序是一个类(即构造函数)。实际上,处理程序唯一需要的是它是可调用的(因此mtraceur的回答是使用partial)。因此,我们可以socketserver通过使处理程序类的实例可调用并让它们__init__在调用时运行超类的代码来解决API 。在 Python 3 中:
class MyHandler(http.server.BaseHTTPRequestHandler):
def __init__(self, message):
self.message = message
def __call__(self, *args, **kwargs):
""" Handle a request """
super().__init__(*args, **kwargs)
def do_GET(self):
self.send_response(200)
self.end_headers()
self.wfile.write(self.message.encode('utf-8'))
Run Code Online (Sandbox Code Playgroud)
这使超类的“构造函数”调用__init__远离,从而消除了在子类的构造函数完成之前分派请求(来自超类的构造函数)的可能性。它还使使用 API 看起来更自然:
handler = MyHandler("Hello world")
server = http.server.HTTPServer(('localhost', 8000), handler)
server.serve_forever()
Run Code Online (Sandbox Code Playgroud)
我只想评论Thomas Orozco的答案,但是因为我不能......也许这会帮助那些也遇到这个问题的人.在Python3之前,Python有"旧式"类,BaseHTTPRequestHandler似乎就是其中之一.所以,工厂应该是这样的
def MakeHandlerClassFromArgv(init_args):
class CustomHandler(BaseHTTPRequestHandler, object):
def __init__(self, *args, **kwargs):
do_stuff_with(self, init_args)
super(CustomHandler, self).__init__(*args, **kwargs)
return CustomHandler
Run Code Online (Sandbox Code Playgroud)
避免像这样的错误TypeError: must be type, not classobj.
参考子类化HTTPServer是另一种选择。服务器上的变量可以通过请求处理程序方法访问self.server.context。它基本上是这样工作的:
class MyHTTPServer(HTTPServer):
def __init__(self, *args, **kwargs):
HTTPServer.__init__(self, *args, **kwargs)
self.context = SomeContextObject()
class MyHandler(BaseHTTPRequestHandler):
def do_GET(self):
context = self.server.context
...
# Drawback, Notice that you cannot actually pass the context parameter during constructor creation, but can do it within the __init__ of the MyHTTPServer
server = MyHTTPServer(('', port), MyHandler)
server.serve_forever()
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
10356 次 |
| 最近记录: |