Python文档包括an example of creating an HTTP server:
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
类的部分来实现这一点,但我觉得我忽略了一个更简单,更好的解决方案。
答案 0 :(得分:22)
使用班级工厂:
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()
答案 1 :(得分:8)
我使用“部分应用程序”在代码中解决了这个问题。
示例是使用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
进行序列化,而工厂函数中的嵌套类定义则不能。partial
将参数显式地“预先附加”到其他Pythonic和普通的类定义上是更容易(较少的认知负担),以便阅读,理解,并验证其正确性,而不是将嵌套函数的参数埋在其中的嵌套类定义。唯一真正的缺点是许多人不熟悉partial
-但以我的经验,每个人都变得熟悉{{1 }}无论如何,因为partial
可以在许多地方弹出,作为一种容易且可组合的解决方案,有时会出乎意料,例如此处。
答案 2 :(得分:7)
我只想评论Thomas Orozco的答案,但因为我不能......
也许这会帮助那些也遇到这个问题的人。在Python3之前,Python有#34; old-style"类,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
以避免TypeError: must be type, not classobj
之类的错误。
答案 3 :(得分:4)
为什么不直接使用RequestHandler?
class RequestHandler(BaseHTTPRequestHandler):
a_variable = None
class Server(HTTPServer):
def serve_forever(self, variable):
self.RequestHandlerClass.a_variable = variable
HTTPServer.serve_forever(self)
def run(server_class=Server, handler_class=RequestHandler):
server_address = ('', 8000)
httpd = server_class(server_address, handler_class)
variable = sys.argv
httpd.serve_forever(variable)
答案 4 :(得分:1)
如果您不需要实例属性,而只需要类属性,则可以使用以下方法:
def run(server_class=HTTPServer, handler_class=BaseHTTPRequestHandler):
server_address = ('', 8000)
httpd = server_class(server_address, handler_class)
httpd.RequestHandlerClass.my_custom_variable = "hello!"
httpd.serve_forever()
或者您可以:
def run(server_class=HTTPServer, handler_class=BaseHTTPRequestHandler):
server_address = ('', 8000)
httpd = server_class(server_address, handler_class)
httpd.my_custom_variable = "hello!"
httpd.serve_forever()
并使用以下命令在您的RequestHandler中进行检索:
self.server.my_custom_variable
答案 5 :(得分:1)
Ref子类化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()
答案 6 :(得分:0)
在撰写本文时,此处的所有答案基本上都出于(非常尴尬)的意图,即socketserver
模块的作者似乎要求传入的处理程序是一个类(即构造函数)。确实,处理程序唯一需要的就是它是可调用的(因此 mtraceur 的答案使用partial
)。因此,可以通过使处理程序类的实例可调用并使它们在调用时运行超类的socketserver
代码来解决__init__
API的问题。在Python 3中:
class MyHandler(http.server.BaseHTTPRequestHandler):
def __init__(self, message):
self.message = message
def __call__(self, *args):
""" Handle a request """
super().__init__(*args)
def do_GET(self):
self.send_response(200)
self.end_headers()
self.wfile.write(self.message.encode('utf-8'))
这使超类“ constructor”的调用不在__init__
中,从而消除了在子类的构造函数完成之前分派请求(来自超类的构造函数)的可能性。这也使使用API看起来更加自然:
handler = MyHandler("Hello world")
server = http.server.HTTPServer(('localhost', 8000), handler)
server.serve_forever()
答案 7 :(得分:-3)
永远不要使用global
。使用其他答案中描述的工厂。
CONFIG = None
class MyHandler(BaseHTTPRequestHandler):
def __init__(self, ...
self.config = CONFIG # CONFIG is now 'stuff'
if __name__ == "__main__":
global CONFIG
CONFIG = 'stuff'
server_address = ('', 8000)
httpd = HTTPServer(server_address, MyHandler)
httpd.serve_forever()
(可能在您自己家中的隐私之外)