Tornado的url-handler映射中可以使用字符串
这个标题真是含糊,因为琢磨了很久都不知道该怎么描述,不过看了下面的说明,就明白到底在说什么了。在使用 Tornado 中,必须将 url-handler 元组组成的列表传给 Application
以便实例化,其中 url-handler 如下面所示:
[
('/', HomeHandler),
('/([a-zA-Z0-9-]+)/*', EntryHandler),
('/picky/([a-zA-Z0-9-]+)/*', PickyHandler),
]
不过在 Django 中,也是使用类似的方法来建立url到相应处理方法的对应,不过 Django 中是这样来做的:
urlpatterns = patterns('',
url(r'^$', 'mysite.views.bar'),
url(r'^mysite/', 'mysite.views.foo'),
)
唯一的区别是 Django 使用的对应处理方法用的是字符串,这样明显的好处就是不用在 urls.py
文件中繁琐的导入需要的模块了,比如上面 Tornado 的例子中,如果在独立的文件中设置映射,就必须像下面这样:
from blog import HomeHandler, EntryHandler, PickyHandler
#上面的映射列表
毫无疑问,这样不是一点麻烦,那么 Tornado 能否像 Django 那样呢,当然可以,在 Tornado 的源代码中找到了答案,代码见这里。它判定 handler
是字符类型,则使用 import_object
函数来通过字符串调用相应的模块,不过需要注意的是,模块需要从项目的根开始,最上面的那个例子属于 blog
这个包下,于是可以这样改写:
[
('/', 'blog.HomeHandler'),
('/([a-zA-Z0-9-]+)/*', 'blog.EntryHandler'),
('/picky/([a-zA-Z0-9-]+)/*', 'blog.PickyHandler'),
]
这样就不用去显示的导入那些该死的模块,瞬间觉得轻松多了。
浅析 Python 字符串形式的模块导入
现在把焦点集中在 import_object
函数上,该函数是这样定义的:
def import_object(name):
parts = name.split('.')
obj = __import__('.'.join(parts[:-1]), None, None, [parts[-1]], 0)
return getattr(obj, parts[-1])
其中用到了 __import__
这个内建函数,主要用来根据字符串导入模块,最大的好处就像上面的那个示例一样,可以使程序在运行时动态加载一些模块,而不是在py文件的开头使用 import
来加载。下面通过几个例子来简单的说明一下这个函数的用法,在 Python 中导入模块有下面这样的形式:
import pkg
import pkg.foo
from pkg import foo, bar
from pkg.foo import func
from pkg.foo import submod
在第一和第二种上,import
导入将使用最左边的模块名来作为使用导入模块的本地名称,也就是说如果要使用 foo
中 func
函数必须这样写 pkg.foo.func()
;如果使用的是第三种,则可以直接使用 foo.func()
。如果使用 __import__
函数来导入,第一、二种形式是一样的,只需要这样:
pkg = __import__('pkg')
pkg = __import__('pkg.foo')
由于 __import__
只会返回一个类型,所以在第三种情况下,需要使用一个临时变量:
tmp = __import__('pkg')
foo = tmp.foo
bar = tmp.bar
不过貌似这样就和我们的初衷不一样了,我们只是想导入 foo|bar
两个子模块,那就需要像下面这样,使用 __import__
的参数 fromlist
:
tmp = __import__('pkg', fromlist=['foo', 'bar'])
mod = tmp.foo
mod2 = tmp.bar
最后一种就比较简单了,就如上面一样,不过我们可以不使用临时变量,使用 getattr
就行了:
submod = getattr(__import__('pkg.foo', fromlist=['submod']), 'submod')
嗯,就这样。