- python 如何将参数传递给 RequestHandler?

  显示原文与译文双语对照的内容

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 类的部分来实现,但是我觉得一个更简单更好的解决方案。

时间:

使用类工厂:


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()



我用"部分应用程序"在代码中解决了这个问题。

示例使用 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 对象比由工厂函数定义和返回的嵌套类更容易内省。
  • 在现代 python 中,partial 对象可以用 pickle 序列化,而工厂函数内嵌套的类定义不能。
  • In limited使用 partial 函数读取correctness理解 correctness 和正常类定义的显式"预连接"explicit比嵌套函数中嵌入函数的参数的嵌套类定义更容易理解 understand 。

我 disadvantage,因为 partial 很多地方都是easy的解决方案,因为在很多地方都是很容易composable的解决方案。

我只会对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



避免错误如 TypeError: must be type, not classobj

为什么不只子类化 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)



如果不需要实例属性,但只有类属性可以使用这里方法:


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



参考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()



不要用 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()



( 可能在你自己家的隐私中除外)

...