【问题标题】:Python - Access "import" from within a classPython - 从类中访问“导入”
【发布时间】:2017-02-09 12:02:08
【问题描述】:

这已经困扰我一段时间了。我正在尝试创建一个非常简单的类似 REST 的界面(不使用我知道可用的第三方库)。

它背后的想法是我可以有一个目录,例如mylib,我可以在其中放入python文件,例如do_something.py,并且通过发布到http://localhost/do_something,代码将变得生动起来并做一些事情!

我想我已经设法通过以下结构接近我的目标:

文件内容如下。

example.py
from http.server import HTTPServer
from http.server import BaseHTTPRequestHandler
import json, logging
from mylib import my_module

class MyRequestHandler (BaseHTTPRequestHandler):

    # Send JSON responses
    # -----------
    def send_json(self, json_message, response_code=200):
        self.send_response(response_code)
        self.send_header('Content-type', 'application/json')
        self.end_headers()
        self.request.sendall(json.dumps(json_message).encode())


    # Get JSON requests
    # ----------
    def get_json(self):
        body = self.rfile.read(int(self.headers.get('Content-Length')))
        if (body):
            try:
                receivedData = json.loads(body.decode())
            except:
                self.send_json({"Status": "Error", "Message": "Invalid JSON received"}, 400)
                receivedData = None
        else:
            receivedData = None
        return receivedData


    # POST
    # ---------
    def do_POST(self):

        module_to_call = (self.path).replace('/', '.')[1:]
        if module_to_call.endswith('.'): # Remove trailing dot
            module_to_call = module_to_call[:-1]
        print("Path is: '" + module_to_call + "'")

        # invoke function
        module_to_call = getattr(self, module_to_call)
        response = module_to_call()
        self.send_json(response)

    # GET
    # --------
    def do_GET(self):

        pass


# -----------------------------------------------------------------------------
# Server startup code
# -------------------
def start_server():


# Begin serving
# -------------
    port = 8003
    server = HTTPServer(('', port), MyRequestHandler)
    print(("Server now running on port {0} ...").format(port))

    server.serve_forever()


# -----------------------------------------------------------------------------
# Start the Server
# ----------------
if __name__ == '__main__':
    start_server()

my_module.py

def my_module():
    print("Hello World!")
    return{'Greeting': 'Hello World!'}

当我启动服务器并尝试 POST 到 http://localhost:8003/my_module 时,我得到以下输出:

Server now running on port 8003 ...
Path is: 'my_module'
----------------------------------------
Exception happened during processing of request from ('127.0.0.1', 59541)
Traceback (most recent call last):
  File "C:\Users\Test\AppData\Local\Programs\Python\Python35-32\lib\socketserver.py", line 313, in _handle_request_noblock
    self.process_request(request, client_address)
  File "C:\Users\Test\AppData\Local\Programs\Python\Python35-32\lib\socketserver.py", line 341, in process_request
    self.finish_request(request, client_address)
  File "C:\Users\Test\AppData\Local\Programs\Python\Python35-32\lib\socketserver.py", line 354, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "C:\Users\Test\AppData\Local\Programs\Python\Python35-32\lib\socketserver.py", line 681, in __init__
    self.handle()
  File "C:\Users\Test\AppData\Local\Programs\Python\Python35-32\lib\http\server.py", line 422, in handle
    self.handle_one_request()
  File "C:\Users\Test\AppData\Local\Programs\Python\Python35-32\lib\http\server.py", line 410, in handle_one_request
    method()
  File ".\example.py", line 43, in do_POST
    module_to_call = getattr(self, module_to_call)
AttributeError: 'MyRequestHandler' object has no attribute 'my_module'
----------------------------------------

这很有意义,因为“MyRequestHandler”没有属性“my_module”!我无法理解的是如何解决这个问题?

我应该将“mylib”传递给 MyRequestHandler 吗?我是否应该在类中执行导入(但该功能只能在类中使用)?

我试图让事情保持干净和简单,这样即使是 Python 新手(就像我一样!)也可以编写一个独立的脚本,将其放入“mylib”中,一切都“正常工作”。新手可以访问他们脚本的网址并神奇地运行它。

我们将不胜感激地收到任何帮助或建议。

【问题讨论】:

  • 由于您将模块导入脚本的全局范围,您可以使用globals()[module_to_call] 从任何函数内部访问它。但是,这不是您真正想要的,因为您手动导入 my_module,如果有人放入新文件,这将无法“正常工作”。
  • 我不知道您尝试做的事情的用例是什么,但我建议您不要重新发明轮子,而是使用轻量级框架(例如 Flask),因为这就是你最终会得到的结果。
  • 这不是一个明显的重复,但最终动态导入将是将其放入“mylib”并且一切“正常工作”的解决方案。
  • 非常感谢@kazemakase!既然您提到了 globals() ,它似乎很明显。你说得对,动态模块导入对我来说并不是最明显的答案(尽管它正盯着我看,去年读过它),但它是我寻求的答案!我不知道如何对您的答案表示赞赏,因为它们是 cmets,而不是答案...无论如何我都赞成他们。

标签: python python-3.x


【解决方案1】:

使用__import__() 方法:

temp = __import__('mylib', globals(), locals(), ['module_to_call'], -1)
response = temp.module_to_call()

我在工作中使用 2.6,这通常被那些甚至使用 2.7 的人使用,因为 importlib 模块在 3 中更加健壮。如果您使用的是 3,您可以执行以下操作:

from importlib import import_module

temp = import_module('mylib')

但是现在你必须使用 getattr 来获取你想要调用的函数

func_to_call = getattr(temp, 'module_to_call')
response = func()

或者您可以在另一个模块中拥有一个函数字典,但随着字典的增长,这将需要大量工作。

【讨论】:

  • 虽然我喜欢这个答案,但我也对它有点警惕,因为我觉得__import__ 的使用有点争议。例如stackoverflow.com/a/26476048/1798547
  • @LesterBurnham 在答案中添加了一些内容,因此您可以查看其他方式。这在很多图书馆都很常见。
猜你喜欢
  • 2012-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-15
相关资源
最近更新 更多