python 装饰器

今天学brython的时候发现了它的ajax请求太繁琐了。比如:

from browser import document, ajax

url = "http://api.open-notify.org/iss-now.json"
msg = "Position of the International Space Station at {}: {}"

def complete(request):
    import json
    import datetime
    data = json.loads(request.responseText)
    position = data["iss_position"]
    ts = data["timestamp"]
    now = datetime.datetime.fromtimestamp(ts)
    document["zone10"].text = msg.format(now, position)

def click(event):    # 就是这个函数的代码,感觉挺没用的,还要敲好几行……一点不符合python的优雅
    req = ajax.ajax()
    req.open("GET", url, True)
    req.bind("complete", complete)
    document["zone10"].text = "waiting..."
    req.send()

document["button10"].bind("click", click)

这是官方给出的代码,然后我在想怎么将这坨代码搞得精简点。于是我想到了装饰器。鉴于我装饰器基础不是很扎实,但是为了以后敲代码的方便,我打算手撕一个python库来一行代码解决ajax的发送问题。为了解决这个问题,我把装饰器学了一遍(都怪我以前偷懒,觉得搞懂原理也没啥用,然后遇到问题了被迫学一遍)。

写这篇博客先主要理解下装饰器。

函数做装饰器

不含参数的装饰器

代码如下:

def hello(func):
    def world():
        pass
    return world

@hello
def a():
    print("hello world!")

装饰器代码等效于:

hello(a)

再精简点,等效于:

world  # 对,就是这个函数名

所以调用这个函数:

a()   # 等效于调用:hello(a)() -> 再等效于调用 world()

这个应该没问题了。

含参的装饰器

这个就直接把我的那个ajax的例子放出来吧:

from browser import ajax, bind, document
from functools import wraps

def send_ajax(url, method="GET", data=None, isasync=True):
    def hello(func):
        @wraps(func)
        def world(*args, **kwargs):
            req = ajax.ajax()
            req.open(method, url, isasync)
            req.bind("complete", func)
            if data is None:
                req.send()
            else:
                req.send(data)
        return world
    return hello

然后使用:

from browser import bind, document
from litter_ajax import send_ajax        # 导入自己写的包

url = "http://api.open-notify.org/iss-now.json"
msg = "Position of the International Space Station at {}: {}"

@bind(document['button10'], 'click')
@send_ajax(url, method="GET", data=None, sync=True)       # 只用多添加一行代码即可!
def complete(request):
    import json
    import datetime
    data = json.loads(request.responseText)
    position = data["iss_position"]
    ts = data["timestamp"]
    now = datetime.datetime.fromtimestamp(ts)
    document["zone10"].text = msg.format(now, position)

不过要注意,ajax要在服务器下才能跨文件执行,可以用brython提供的服务器命令:

py -m http.server

这样就可以了!对了,还有关键的没说:

  1. 如果同时修饰两个装饰器,会先执行最下面的那个装饰器,然后再执行上面的装饰器。
  2. 如果是含参数的装饰器,一般有三层闭包,第一层写所有相关的参数,第二层写函数名,第三层写调用时候传的参数。

好了,可以继续了。

用类做装饰器

不含参数的装饰器

和函数没啥区别,就是多了个__call__函数:

class Hello:
    def __init__(self, func):
        self.func = func
    
    def __call__(self, *args, **kwargs):
        pass

对,和函数没啥区别,知道__call__是干啥的这坨代码就没了。

含参数的装饰器

还是拿我的代码举例子,如下:

class send_ajax:
    def __init__(self, url, method="GET", data=None, sync=True):
        self.url = url
        self.method = method
        self.data = data
        self.sync = sync

    def __call__(self, func):
        @wraps(func)
        def world(*args, **kwargs):
            req = ajax.ajax()
            req.open(self.method, self.url, self.sync)
            req.bind("complete", func)
            if self.data is None:
                req.send()
            else:
                req.send(self.data)
        return world

看懂了函数的代码想必这个代码也好理解。

好了,结束了 ~

2022/5/2 【新增】

装饰器里面当定义函数的时候外面的层数的代码会被执行,最内部层数的代码在函数每次调用的时候执行。

不妨把最里面的设为第一层,一次第二层、第三层。含参的装饰器有三层,不含参的装饰器有两层。

多个装饰器修饰同一个函数的时候,外部层数执行顺序:层数大的优先执行,如果相同层数,第三层是从上到下执行,第二层是从下到上执行

第一层在函数调用的时候执行。函数调用的时候,最上面装饰器的第一层里面的代码会被执行,下面的装饰器第一层的代码不会执行。但是最上面的装饰器第一层里面的代码可以调用紧接着下面一层的装饰器第一层里面的代码,然后得到返回值,直到调用到最后的函数。

总之,执行顺序,第三层从上到下,第二层从下到上,第一层从上到下。

posted @ 2021-06-22 02:49  小默同学  阅读(52)  评论(0编辑  收藏  举报