NiceLeeのBlog 用爱发电 bilibili~

Python 关于装饰器(以web地址解析为例)

2021-02-02
nIceLee

阅读:


例如Flask,在处理方法上加一个@app.route('xxx')即可。
这是怎么实现的呢?

前言

开始的时候思路有点飘,想的是这个思路:

  1. 扫描指定的包
  2. 动态获取module内的属性和方法
  3. 判断方法是否有装饰器,装饰器是否匹配,以及获取装饰器的参数
  4. 根据收集的方法和相应参数处理网络请求

搜了很多,感觉这两篇有点东西:

但这个路数实际上有点歪,本质上还是对装饰器理解不够透彻啊。。

实现

在写装饰器的时候,注意在函数装配时,将参数、函数等相关信息写入解析链即可。
这样在来了请求时,根据解析链逐条匹配进行处理。

web/url.py

from urllib.parse import urlparse, parse_qs
from functools import wraps
import inspect

chains = {}

class get(object):
    def __init__(self, url = ""):
        self.url = url
        
    def __call__(self, func):
        @wraps(func)
        def run(*args, **kwargs):
            return func(*args, **kwargs)
        # 写入函数的参数,供解析的时候初始化使用
        run.params = inspect.signature(func).parameters
        # 加入解析链
        chains[self.url] = run
        return run

def deal_http_get(url: str):
    for url_pattern, func in chains.items():
        if(url.startswith(url_pattern)):
            # 从url解析出参数信息
            params = parse_qs(urlparse(url).query)
            kargs = {}
            # 初始化参数
            for param, value in func.params.items():
                final_val = None
                if param in params:
                    # 从url里面取
                    final_val = params[param][0]
                elif value.default != inspect._empty:
                    # 没有的话再尝试从默认值里面取
                    final_val = value.default
                # 进行赋值
                kargs[param] = final_val
            func(**kargs)
            break;
    

url_test.py

from web import url

@url.get(url = "/hello")
def sayHello(key = '你好'):
    print("sayHello: ", key)

@url.get(url = "/fxxk")
@url.get(url = "/fccku")
def sayJ8(key):
    print("sayJ8: ", key)
        
    
@url.get(url = "/goodbye")
@url.get(url = "/byebye")
def sayBye(key = '再见'):
    print("sayBye: ", key)
    
    
if __name__ == "__main__":
    #假设来了HTTP GET请求
    url_path = "/hello"
    url.deal_http_get(url_path)

    url_path = "/hello?key=哈哈哈"
    url.deal_http_get(url_path)

    url_path = "/fxxk"
    url.deal_http_get(url_path)
    
    url_path = "/fccku?key=fxxk u"
    url.deal_http_get(url_path)

    url_path = "/goodbye"
    url.deal_http_get(url_path)
    
    url_path = "/byebye?key=呜呜呜"
    url.deal_http_get(url_path)

示例运行结果


内容
隐藏