python- - 函数 - - 装饰器函数

目录

  • 装饰器的形成过程
  • 装饰器的使用方式
    • 装饰器 - - 带参数的装饰器
    • 装饰器 - - 带有不定参数的装饰器
    • 装饰器 - - 带返回值的装饰器
    • 装饰器 - - 三层嵌套函数
    • 装饰器 - - 多个装饰器装饰同一个函数
    • 查看函数信息的一些方法
    • 完美的装饰器
  • 装饰器主要功能和装饰器固定结构
  • 开放封闭原则
  • 相关练习题

1,装饰器的形成过程

1.1 统计函数执行的时间

1.1.1 初步实现

import time
def timer(func):
    start = time.time()
    func()
    end = time.time()
    print(end - start)

def func1():
    print("in func1")
    time.sleep(0.1)
timer(func1)

# 结果呈现
in func1
0.10027360916137695

1.1.2 实现调用func1,但是能实现调用timer方法的效果

import time
def timer(func):
    def inner():
        start = time.time()
        func()
        end = time.time()
        print(end - start)
    return inner

def func1():
    print("in func1")
    time.sleep(0.2)
func1 = timer(func1)
func1()

# 结果呈现
in func1
0.20004010200500488

现在已经基本上完美了,唯一碍眼的那句话就是还要在做一次赋值调用

1.1.2装饰器 - - 语法糖

import time
def timer(func):
    def inner():
        start = time.time()
        func()
        end = time.time()
        print(end - start)
    return inner

@timer      # ==> func1 = timer(func1)
def func1():
    print("in func1")
    time.sleep(0.3)

func1()

# 结果呈现
in func1
0.3002743721008301

2,装饰器的使用方式

2.1 装饰器 - - 带参数的装饰器

import time
def timer(func):
    def inner(a):
        start = time.time()
        func(a)
        end = time.time()
        print(end - start)
    return inner

@timer      # ==> func1 = timer(func1)
def func1(a):
    print("%s in func1" % a)
    time.sleep(0.3)

func1(1)

# 结果呈现
1 in func1
0.4000673294067383

2.2 装饰器 - - 带有不定参数的装饰器

import time
def timer(func):
    def inner(*args, **kwargs):
        start = time.time()
        func(*args, **kwargs)
        end = time.time()
        print(end - start)
    return inner

@timer      # ==> func1 = timer(func1)
def func1(a,b):
    print("in func1")
    time.sleep(0.1)

@timer
def func2(a):
    print("in func 2 and get a:%s" % a)
    time.sleep(0.1)


func1("aaaaa", "bbbbb")
func2("aaa")

# 结果呈现
in func1
0.10031294822692871
in func 2 and get a:aaa
0.10035037994384766

2.3 装饰器 - - 带返回值的装饰器

import time
def timer(func):
    def inner(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(end - start)
        return result
    return inner

@timer      # ==> func1 = timer(func1)
def func1(a,b):
    print("in func1")
    time.sleep(0.1)
    return "func1 over"

@timer
def func2(a):
    print("in func 2 and get a:%s" % a)
    time.sleep(0.1)
    return "func2 over"

print(func1("aaaaa", "bbbbb"))
print("*" * 20)
print(func2("aaa"))

# 结果呈现
in func1
0.10034704208374023
func1 over
********************
in func 2 and get a:aaa
0.10026073455810547
func2 over

2.4 装饰器 - - 三层嵌套函数

def outer(flag):
    def wrapper(func):
       def inner(*args, **kwargs):
           """在函数被调用之前添加的代码"""
           ret = func(*args, **kwargs)
           """在函数被调用之后添加的代码"""
           return ret
       return inner
    return wrapper

@ outer(True)
def func():
    print("111")

func()

# 结果呈现
111

2.5 装饰器 - - 多个装饰器装饰同一个函数


def wrapper1(func):     # func --> f
    def innet1():
        print("wrapper1, before func")
        ret = func()      # f
        print("wrapper1, after func")
        return ret
    return innet1

def wrapper2(func):     # func --> inner1
    def innet2():
        print("wrapper2, before func")
        ret = func()      # inner1
        print("wrapper2, after func")
        return ret
    return innet2


@wrapper2       # f = wrapper2(f) --> wrapper2(inner1)  == inner2
@wrapper1       # f = wrapper1(f) == inner1
def f():
    print("in f")
    return "哈哈哈"
print(f())         # == inner2()

# 结果呈现
wrapper2, before func
wrapper1, before func
in f
wrapper1, after func
wrapper2, after func
哈哈哈

2.6 查看函数信息的一些方法

def index():
    """这个一个测试信息"""
    print("test index")

print(index.__name__)   # 查看函数名的方法
print(index.__doc__)    # 查看函数注释的方法

# 结果呈现
index
这个一个测试信息

2.7 完美的装饰器

from functools import wraps
def wrapper(func):
    @wraps(func)
    def inner(*args, **kwargs):
        print("在被装饰的函数执行之前做的事")
        result = func(*args, **kwargs)
        print("在被装饰的函数执行之后做的事")
        return result
    return inner

@wrapper        # holiday = wrapper(holiday)
def holiday(day):
    """
    注释:放假
    :param day:
    :return:
    """
    print("全体放假 %s 天" % day)
    return "好开心"

ret = holiday(3)
print(ret)
print("*" * 50)
print(holiday.__name__)
print(holiday.__doc__)

# 结果呈现
在被装饰的函数执行之前做的事
全体放假 3 天
在被装饰的函数执行之后做的事
好开心
**************************************************
holiday

    注释:放假
    :param day:
    :return:

3,开放封闭原则

1.对扩展是开放的

  • 为什么要对扩展开放呢?
    • 我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。

2.对修改是封闭的

  • 为什么要对修改封闭呢?
    • 就像我们刚刚提到的,因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个时候我们对其进行了修改,很有可能影响其他已经在使用该函数的用户。

装饰器完美的遵循了这个开放封闭原则。

4,装饰器的主要功能和装饰器的固定结构

  • 装饰器的本质:一个闭包函数
  • 装饰器的作用:在不改变原来函数的调用方式的基础上,在这个函数的前、后添加新的功能

装饰器的固定格式

# 基础的装饰器
def wrapper(func):
   def inner(*args, **kwargs):
       """在函数被调用之前添加的代码"""
       ret = func(*args, **kwargs)
       """在函数被调用之后添加的代码"""
       return ret
   return inner
def wrapper(func):
   def inner(*args, **kwargs):
       """在函数被调用之前添加的代码"""
       ret = func(*args, **kwargs)
       """在函数被调用之后添加的代码"""
       return ret
   return inner
# 完美的装饰器
from functools import wraps
def wrapper(func):
   @wraps(func)
   def inner(*args, **kwargs):
       """在函数被调用之前添加的代码"""
       ret = func(*args, **kwargs)
       """在函数被调用之后添加的代码"""
       return ret
   return inner

5,相关练习题

5.1 编写装饰器,为多个函数加上记录功能,要求每次调用函数都将被调用的函数名称写入文件

def log(func):
    def inner(*args, **kwargs):
        with open("hello_world_log", "a", encoding="utf-8") as f:
            f.write(func.__name__ + "\n")
        result = func(*args, **kwargs)
        return result
    return inner

@log
def shoplist_add():
    print("增加一件物品")

@log
def shoplist_del():
    print("删除一件物品")

shoplist_add()
shoplist_del()

5.2 编写下载网页内容的函数,要求功能是:用户传入一个 url,函数返回下载页面的结果

from urllib.request import urlopen
def get(url):
    code = urlopen(url).read
    return code

result = get("https://www.baidu.com")
print(result)

5.3为上个题目写装饰器,实现缓存网页内容的功能:

具体:实现下载的页面存放于文件中,如果文件内有值(文件大小不为0),就优先从文件中读取网页内容,否则,就去下载,然后

import os
from urllib.request import urlopen

def cache(func):
    def inner(*args, **kwargs):
        if os.path.getsize("hello_world_web_cache"):
            with open("hello_world_web_cache","rb") as f:
                return f.read()
        ret = func(*args, **kwargs)
        with open("hello_world_web_cache","wb") as f:
            f.write(b"***" + ret)
        return ret
    return inner

@cache
def get(url):
    code = urlopen(url).read()
    return code

result = get("https://www.baidu.com")
print(result)
  • 转自:http://www.cnblogs.com/Eva-J/articles/7194277.html