如何扩展 python3 BaseHTTPRequestHandler class 以便可以使用成员变量调用函数

How to extend python3 BaseHTTPRequestHandler class so that can call function with member variable

我正在尝试使用我自己的成员变量扩展 BaseHTTPRequestHandler。我将这些成员变量传递给自由函数但是当我这样做时我得到:

Exception happened during processing of request from ('127.0.0.1', 30006)
Traceback (most recent call last):
  File "c:\Python37\lib\socketserver.py", line 313, in _handle_request_noblock
self.process_request(request, client_address)
  File "c:\Python37\lib\socketserver.py", line 344, in process_request
    self.finish_request(request, client_address)
  File "c:\Python37\lib\socketserver.py", line 357, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "fruiterer.py", line 41, in __init__
    BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
  File "c:\Python37\lib\socketserver.py", line 712, in __init__
    self.handle()
  File "c:\Python37\lib\http\server.py", line 426, in handle
    self.handle_one_request()
  File "c:\Python37\lib\http\server.py", line 414, in handle_one_request
    method()
  File "fruiterer.py", line 62, in do_POST
    fruit_handler(val, Event_dictionary_list, cached_timestamp)
NameError: name 'Event_dictionary_list' is not defined

这里是 python3 源代码:

# sample python HTTP POST handler

__metaclass__= type
from http.server import BaseHTTPRequestHandler, HTTPServer  
import os  

import time
import threading    
import requests
import json

def calculate(Event_dictionary_list, cached_timestamp):
    # do a calculation here based on fruits in Event_dictionary_list
    print("there are: %i items" % len(Event_dictionary_list))
    Event_dictionary_list.clear() #- empty Event_dictionary_list
    cached_timestamp = 0   

def Async_func(Event_dictionary_list, cached_timestamp):
    calculate(Event_dictionary_list, cached_timestamp)

def fruit_handler(event, Event_dictionary_list, cached_timestamp):
    if not Event_dictionary_list: #Checking if Event_dictionary_list is empty 
        # cache first item added
        cached_timestamp = event['timestamp']
        #- set a 30 second timer
        threading.Timer(60,Async_func, Event_dictionary_list, cached_timestamp).start()

    # make decision as to when to calculate
    if event['timestamp'] - cached_timestamp < 60*1000: #- in milliseconds
        # append event to list
        Event_dictionary_list.append(event)


#############################################################################################################
# we create a server to handle POST requests from Xovis
class fruiterer(BaseHTTPRequestHandler):
    def __init__(self, *args, **kwargs):
         BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
         self.Event_dictionary_list = []
         self.cached_timestamp = 0

     # we only need to support he POST http method
    def do_POST(self):
        #print("post msg received");
        self.data_string = self.rfile.read(int(self.headers['Content-Length']))

        self.send_response(200) # 200 = success - thanks for message response
        #self.send_header() #'Content-type','text-html')
        self.end_headers()

        data = json.loads(self.data_string)

        # we assume json will be decoded as object, eg:
        # {"fruit":{"timestamp":1538688902037,"name":"apple","colour":"red","weight":100}}
        if type(data) is dict:
               for key, val in data.items():
                 # we are only interested in fruits
                 if key == "fruit":
                   fruit_handler(val, Event_dictionary_list, cached_timestamp)
                   break

        return

def run():
    print('http server is starting...')
    # change port to listen on here - arbitrarily using port 7777
    port = 7777
    server_address = ('127.0.0.1', port)  
    #use the code from here .start()
    httpd = HTTPServer(server_address, fruiterer)  
    print('http server is listening on port %d' % port)  

    httpd.serve_forever()    

if __name__ == '__main__':  
  run()

这可以通过在 POST 正文中发送以下 json 格式来测试:

{"fruit":{"timestamp":1538688902037,"name":"apple","colour":"red","weight":100}}

我做错了什么?

您需要从 do_POST() 方法中传入 self.Event_dictionary_list,而不是 Event_dictionary_list。这同样适用于 cached_timestamp.

然后您会发现您将在 self.Event_dictionary_list 上获得 AttributeError,因为该属性从未在您的 __init__ 方法中设置。文档对此有点含糊,但调用 BaseHTTPRequestHandler.__init__(self, *args, **kwargs) 会触发单个请求的处理,因此该方法不会 return 直到 请求处理程序之后已经完成了。

将那些属性的设置移动到之前你调用基础__init__方法:

class fruiterer(BaseHTTPRequestHandler):
    def __init__(self, *args, **kwargs):
         self.Event_dictionary_list = []
         self.cached_timestamp = 0
         super().__init__(*args, **kwargs)

我在这里使用了 super().__init__() 语法以允许将来进一步的合作继承。如果你真的愿意,你仍然可以使用 BaseHTTPRequestHandler.__init__(self, *args, **kwargs),但上面的内容不那么冗长。

请注意每个单独的请求 self.Event_dictionary_list = [] 是 运行。

如果此对象在您的 Python 程序(与服务器 运行ning 一起)期间应存在,请改为将其设为 class 属性:

class fruiterer(BaseHTTPRequestHandler):
    Event_dictionary_list = []
    cached_timestamp = 0

另一个问题是在其他函数中将 cached_timestamp 设置为 cached_timestamp = ... 只会改变局部变量。整数是不可变对象,因此您只能将新整数赋值给变量,但您调用的函数中的局部变量不与请求处理程序共享命名空间。

但是如果您希望该值在所有请求中保持不变,那么您可以在任何地方引用 fruiterer.cached_timestamp 并直接分配给该属性。