廖大python实战项目第五天

PS: 决定还是坚持写博客记录一下比较好。

今天的实战内容是编写web框架,如果之前的知识不熟悉的话确实看不大懂。在这里奉上自己的理解以及帮助理解的相关资料和文档。

索引

    • 索引
      • Web框架
      • __call__方法
      • getattr方法
      • inspect模块
      • rfind方法
      • __import__
        • 拦截器


Web框架

首先我们要知道web框架是什么东西,它到底要怎么实现。这一点廖大在web开发的WSGI接口、使用web框架这两篇文章里已经说过了。摘要一些略作说明:

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return [b'

Hello, web!

'
]

上面这段代码实现了响应HTTP请求,其中start_response 发送了HTTP响应的Headerreturn 则将HTTP响应的body 返回。在这里面,如何接受、解析HTTP请求和发送HTTP响应不是我们关注的重点,我们该做的是决定用什么内容去响应HTTP请求,而其余部分交给WSGI接口去实现。这里已经出现了第一层的抽象,让我们摆脱烦杂的底层逻辑。

然而单单实现这个还是不够的,对比WebApp的处理逻辑,这里还是有些低级。一个url对应一个HTTP请求,而HTTP请求可分为GET,POST,PUT,DELETE等等,我们自己用一个一个的函数实现不大现实。所以我们需要对WSGI接口做进一步的抽象,也就是所谓的Web框架

回到廖大的这一天的实战内容,是要对aiohttp封装一个更高层次的web框架。为了看懂aiohttp实现HTTP响应的逻辑(整体和同步一样但是还是有一些差别影响理解),我们需要先把之前的异步IO那四篇都回顾一下,在这里,aiohttp官方文档还有Server Usage可以帮助我们更好地理解request,Response,Application这三个方法的功能。

涉及到模块渲染装饰器我一开始有些不清楚,补一下对应的章节就明白了。


__call__()方法

廖大提到一个方法__call()__,只要一个类定义了这个方法,就可以将它的实例视为函数。这里先上一篇文章Python的特殊函数 __call()__算是讲得很清楚了。python中函数是一个对象,与普通类的实例不同的是,函数是callable;但只要给一个类添上__call()__方法,就可以实现像函数那样的调用。


getattr()方法

这个方法在前两天的实战中已经见过,但还是补一下资料内容。文章:Python的hasattr(), getattr(), setattr()函数使用方法详解。

getattr()是用于获取对象的属性或方法,如果存在就打印出来,如果不存在就打印出默认值。需要注意的是,如果是返回的对象的方法,返回的是方法的内存地址,如果需要运行这个方法,可以在后面添加一对括号。


inspect模块

参考:
Python文档–inspect模块介绍
中文版inspect模块介绍

在这段实例中:

def add_route(app, fn):
    method = getattr(fn, '__method__', None)
    path = getattr(fn, '__route__', None)
    if path is None or method is None:
        raise ValueError('@get or @post not defined in %s.' % str(fn))
    if not asyncio.iscoroutinefunction(fn) and not inspect.isgeneratorfunction(fn):
        fn = asyncio.coroutine(fn)
    logging.info('add route %s %s => %s(%s)' % (method, path, fn.__name__, ', '.join(inspect.signature(fn).parameters.keys())))
    app.router.add_route(method, path, RequestHandler(app, fn))

廖大用到了之前没见过的inspect模块。其中inspect.isgeneratorfunction()好理解,从字面上可以知道是判断是否为generaor函数。具体的实现暂时不去管它。

后面那个inspect.signature(fn).parameters.keys()有点费解。其中inspect.signature()的功能根据官方文档讲是Return a Signature object for the given callable,即返回传入的可调用函数的所有参数;而之后的paramters似乎是用于索引参数。这是官方的两个示例:

关于signature

>>> from inspect import signature
>>> def foo(a, *, b:int, **kwargs):
...     pass

>>> sig = signature(foo)

>>> str(sig)
'(a, *, b:int, **kwargs)'

>>> str(sig.parameters['b'])
'b:int'

>>> sig.parameters['b'].annotation
<class 'int'>

关于parameter

>>> def foo(a, b, *, c, d=10):
...     pass

>>> sig = signature(foo)
>>> for param in sig.parameters.values():
...     if (param.kind == param.KEYWORD_ONLY and
...                        param.default is param.empty):
        # KEYWORD_ONLY: 关键字参数
...         print('Parameter:', param)
Parameter: c

所以,廖大的那段代码的意思是将函数的所有参数(不包括默认值)用逗号隔开,加入到那个括号里去。


rfind()方法

从右向左查询,返回字符串首次出现的位置;如果没有匹配项则返回-1。
find()与之类似,只不过是从左向右查询。


__import__()

参考:
Python官方文档
__import__中文介绍

__import__内置函数是用于动态加载模块的。这个不难理解,关键是理解廖大的示例:

def add_routes(app, module_name):
    n = module_name.rfind('.')  # 找到模块名的最后一个'.'位置
    if n == (-1):               # 如果模块名为“XX”这种形式
        mod = __import__(module_name, globals(), locals())
    else:                       # 如果模块名为"XX.XX"这种形式
        name = module_name[n+1:] # 后半部分即子函数
        mod = getattr(__import__(module_name[:n], globals(), locals(), [name]), name)         # 加载模块,获取模块的子函数并返回
    for attr in dir(mod):        # 对于子函数里的所有属性和方法
        if attr.startswith('_'): # 如果attr为”__XX"这种形式就忽略
            continue
        fn = getattr(mod, attr)
        if callable(fn):         # 如果可调用
            method = getattr(fn, '__method__', None)
            path = getattr(fn, '__route__', None)
            if method and path:
                add_route(app, fn)

拦截器

拦截器

你可能感兴趣的:(python)