3

我试图在 Python 中调用基于字符串的方法,类似于 Tornado 基于 URL 路由请求的方式。例如,如果我有字符串“/hello”,我想在某个类中调用方法“hello”。我一直在研究 Python Routes 并提出了以下解决方案:

from routes import Mapper

class Test():
    def hello(self):
        print "hello world"

map = Mapper()
map.connect(None, '/hello', controller=Test(), action='hello')

match = map.match("/hello")
if match != None:
    getattr(match['controller'], match['action'])()

但是,我想添加将部分字符串作为参数传递给方法的功能。例如,我想调用字符串“hello/5” hello(5)(再次非常类似于 Tornado 的路由)。这应该适用于任意数量的参数。有没有人对此有好的方法?是否可以在不是请求 URL 的字符串上使用 Tornado 的路由功能?

4

2 回答 2

1

相似的想法,不同的方式。这是代码:

class Xroute(tornado.web.RequestHandler):
    def get(self,path):
        Router().get(path,self)
    def post(self,path):
        Router().post(path,self)

class Router(object):
    mapper={}
    @classmethod
    def route(cls,**deco):
        def foo(func):
            url = deco.get('url') or '/'
            if url not in cls.mapper:
                method = deco.get('method') or 'GET'
                mapper_node = {}
                mapper_node['method'] = method
                mapper_node['call'] = func
                cls.mapper[url] = mapper_node
            return func
        return foo

    def get(self,path,reqhandler):
        self.emit(path,reqhandler,"GET")

    def post(self,path,reqhandler):
        self.emit(path,reqhandler,"POST")

    def emit(self,path,reqhandler,method_flag):
        mapper = Router.mapper
        for urlExp in mapper:
            m = re.match('^'+urlExp+'$',path)
            if m:
                params = (reqhandler,)
                for items in m.groups():
                    params+=(items,)
                mapper_node = mapper.get(urlExp)
                method = mapper_node.get('method')
                if method_flag not in method:
                    raise tornado.web.HTTPError(405)
                call = mapper_node.get('call')
                try:
                    call(*params)
                    break
                except Exception,e:
                    print e
                    raise tornado.web.HTTPError(500)
        else:
            raise tornado.web.HTTPError(404)

@Router.route(url=r"hello/([a-z]+)",method="GET")
def test(req, who):
    #http://localhost:8888/hello/billy
    req.write("Hi," + who)

@Router.route(url=r"greetings/([a-z]+)",method="GET|POST")
def test2(req, who):
    #http://localhost:8888/greetings/rowland
    test(req, who)

@Router.route(url=r"book/([a-z]+)/(\d+)",method="GET|POST")
def test3(req,categories,bookid):
    #http://localhost:8888/book/medicine/49875
    req.write("You are looking for a " + categories + " book\n"+"book No. " + bookid)

@Router.route(url=r"person/(\d+)",method="GET|POST")
def test4(req, personid):
    #http://localhost:8888/person/29772
    who = req.get_argument("name","default")
    age = req.get_argument("age",0)
    person = {}
    person['id'] = personid
    person['who'] = who
    person['age'] = int(age)
    req.write(person)

if __name__=="__main__":
    port = 8888
    application = tornado.web.Application([(r"^/([^\.|]*)(?!\.\w+)$", Xroute),])
    application.listen(port)
    tornado.ioloop.IOLoop.instance().start()
于 2014-03-14T07:16:17.680 回答
1

首先,最好知道制作这样一个“本地”路由系统的动机、用例和初始任务是什么。

依靠您的示例,一种方法是自己实际构建一些东西。

这是一个非常基本的示例:

class Test():
    def hello(self, *args):
        print args


urls = ['hello/', 'hello/5', 'hello/5/world/so/good']
handler = Test()

for url in urls:
    parts = url.split('/')
    method = parts[0]
    if hasattr(handler, method):
        getattr(handler, method)(*parts[1:])

印刷:

('',)
('5',)
('5', 'world', 'so', 'good')

您还可以使用正则表达式将部分 url 保存为命名组或位置组。作为一个例子,看看djangopython-routes是如何做到的。

希望有帮助。

于 2014-03-09T06:01:24.427 回答