Python 如何将参数传递给RequestHandler?

Python 如何将参数传递给RequestHandler?,python,Python,Python文档包括: 一个RequestHandler类被提供给服务器,然后该服务器负责自动实例化处理程序 假设我想在创建请求处理程序时将自定义参数传递给它。我该怎么做 更具体地说,我希望从命令行传入参数,并且必须访问请求处理程序类中的sys.argv,这似乎是不必要的笨拙 这似乎可以通过重写服务器类的某些部分来实现,但我觉得我忽略了一个更简单更好的解决方案。使用类工厂: def MakeHandlerClassFromArgv(init_args): class CustomHand

Python文档包括:

一个
RequestHandler
类被提供给
服务器
,然后该服务器负责自动实例化处理程序

假设我想在创建请求处理程序时将自定义参数传递给它。我该怎么做

更具体地说,我希望从命令行传入参数,并且必须访问请求处理程序类中的
sys.argv
,这似乎是不必要的笨拙

这似乎可以通过重写
服务器
类的某些部分来实现,但我觉得我忽略了一个更简单更好的解决方案。

使用类工厂:

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

为什么不将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)

我只想评论托马斯·奥罗斯科的回答,但因为我不能。。 也许这会帮助其他也遇到这个问题的人。在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这样的错误:必须是type,而不是classobj

切勿使用
全局
执行此操作。使用其他答案中描述的工厂

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

(除非是在你自己家的隐私中)

我在代码中使用“部分应用程序”解决了这个问题

示例使用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
    进行序列化,而工厂函数中的嵌套类定义则不能(至少不能不在类上编写一个
    \uuuuu reduce\uuuu
    方法使之成为可能)
  • 根据我有限的经验,将带有
    partial
    的参数显式地“预附加”到其他类似于Pythonic和普通类定义中是更容易阅读、理解的,并验证嵌套类定义的正确性,该嵌套类定义中隐藏了包装函数的参数

唯一真正的缺点是许多人不熟悉
partial
——但根据我的经验,大家最好还是熟悉
partial
,因为
partial
在很多地方都有一种方法可以作为一种简单且可组合的解决方案出现,有时是出乎意料的,如图所示。

如果您不需要实例属性,而只需要类属性,则可以使用此方法:

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

在撰写本文时,这里的所有答案基本上都坚持(非常尴尬)的意图,
socketserver
模块的作者似乎认为传入的处理程序是一个类(即构造函数)。实际上,处理程序唯一需要的是它是可调用的(因此mtraceur的答案是使用
partial
)。因此,我们可以绕过
socketserver
API,方法是使处理程序类的实例可调用,并让它们在调用时运行超类的
\uuuuuu init\uuuu
代码。在Python 3中:

类MyHandler(http.server.BaseHTTPRequestHandler):
定义初始化(自我,消息):
self.message=消息
定义调用(self,*args,**kwargs):
“”“处理请求”“”
super()
def do_获得(自我):
自我发送_响应(200)
self.end_头()
self.wfile.write(self.message.encode('utf-8'))
这使得超类“构造函数”调用不在
\uuuu init\uuuu
中,从而消除了在子类的构造函数完成之前(从超类的构造函数)调度请求的可能性。它还使API的使用看起来更自然:

handler=MyHandler(“你好,世界”)
server=http.server.HTTPServer(('localhost',8000),处理程序)
服务器。永远为您服务()

handler类为什么不子类化?这似乎是socketserver库中的一个主要缺陷design@TamasHegedus我也这么想,直到我想起了分部应用程序的优雅,它解决了向任何可调用函数传递附加参数的一般情况/问题,没有任何其他代码必须在其接口和实现中重新实现相同的参数传递逻辑。简单地将自己的init添加到处理程序中是不可行的。我已经有了一个
RequestHandler
子类。我是否可以向它传递命令行参数,而不向其中硬编码
sys.argv
?这样做将类与全局变量和脚本的命令行参数紧密地结合在一起(例如,降低了可测试性)。实际上,在本例中,我忽略了您使用的
sys.argv
。我会仔细看看这个方法,并尝试一下。是的,你可以。也许你们应该研究一下多处理库。我过去所做的(不是针对命令行参数,但我想您可以做类似的事情)是创建一个队列(),该队列在RequestHandler进程和main之间共享,然后RequestHandler等待QEvent。因此在本例中,我不会将参数传递到构造函数中,而是传递到其他地方?@Jakob实际上,您不需要再传递参数了,因为创建类时它们在范围内。查看我的编辑。很好的解决方案。我想应该是:self。用self做东西(init_args)。我明白了。顺便说一句,它只在我添加self时起作用。在调用super之前用self(init_args)填充self。
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()