函数进阶四(装饰器、内置函数)

. 昨日内容回顾

生成器,本质就是迭代器,自己用Python代码构建的迭代器
1.生成器函数(yield)
2.生成器表达式(i for i in range(21))

生成器对象:gentor
next(gentor)
send(gentor)
for 循环

yield 与 return 的区别

send:
给上一个yield发送一个值
send 对应yield取值

next:
对应一个yield取值

yield from——将一个可迭代对象变成一个迭代器返回

列表推导式
循环模式:[变量(加工后的变量) for 变量 in iterable]
筛选模式:[变量(加工后的变量) for 变量 in iterable if 条件]
三元模式:["*" if a > b else 变量 for 变量 in iterable]

优点:
一行代码搞定
缺点:不能使用debug模式查错

生成器表达式:与列表推导式几乎一样,只需将[]改为()即可

字典推导式、集合推导式


二. 装饰器
# 装饰器

# 假设现在要写一个程序来测试某段代码的运行效率
import time
# 这是要测试的代码段,一个函数
def func():
    print("测试这个函数的执行效率...")
    # 因为该函数几乎没什么代码,所以加个延时
    time.sleep(0.2)

func()

# 现在开始先测试程序
def timmer():
    start_time = time.time()
    func()  # 注意是函数调用开始计时
    end_time = time.time()
    print("该函数的执行效率%s" % (end_time - start_time))

timmer()

# 第一版完成
def timmer(f):
    start_time = time.time()
    f()  # 注意是函数调用开始计时
    end_time = time.time()
    print("该函数的执行效率%s" % (end_time-start_time))

timmer(func)

# 上面这样只能测试一个人的,如果要测试三个人的,又得这样:
def func1():
    print("测试这个函数的执行效率...")
    # 因为该函数几乎没什么代码,所以加个延时
    time.sleep(0.2)

def func2():
    print("测试这个函数的执行效率...")
    # 因为该函数几乎没什么代码,所以加个延时
    time.sleep(0.2)

def func3():
    print("测试这个函数的执行效率...")
    # 因为该函数几乎没什么代码,所以加个延时
    time.sleep(0.2)

# timmer(func1)
# timmer(func2)
# timmer(func3)

# 上一个版本中,假如有500个函数的项目,那么项目中可能出现5000次调用
# 如果想要不影响项目正常运作的情况下测试每个函数的执行效率,意味着将项目中5000次这样的调用方式func()变成timmer(func)


# 这样明显不行,要尽量不改变原函数的调用方式,并且能对原函数进行效率测试
# 即原函数既要正常运行,又能测试它的时间

import time
def func():
    print("测试这个函数的执行效率...")
    time.sleep(0.2)

def timmer(f):
    start_time = time.time()
    f()
    end_time = time.time()
    print("该函数的执行效率是%s" % (end_time - start_time))

func()
f1 = func  # 这里表示 f1 和 func 在内存中指向同一个内存地址
func = timmer  # 这里开始,func 指向函数 timmer 在内存中的地址,但是 f1 还是指向原先 func 函数的内存地址
func(f1)  # 因此相当于timmer(func),没有返回值,相当于做了一个伪装

# 上一个版本虽然做了伪装,执行原函数的调用方式不变,并且测试了执行效率
# 但是这里还有个问题,如果func()里面有参数的话,这样就伪装不彻底了
# 而且 func(f1) 上面还有两行代码,如果要测试500个函数就要多写至少1000行代码
# 装饰器的雏形

import time
def func():
    print("测试这个函数的执行效率...")
    time.sleep(0.2)

def timmer(f):
    def inner():
        start_time = time.time()
        f()
        end_time = time.time()
        print("该函数的执行效率是%s" % (end_time - start_time))
    return inner

func = timmer(func)  # inner

# # 相当于 f = (func函数),但是不执行 inner 函数,因为没有调用它(inner()),只是返回了 inner 这个函数名
# # 也就是(return innner),所以这里相当于 inner 这个函数名赋值给一个变量 func
# func()  # inner()
# 这里就是在调用 inner(),因为 inner() 中又有 f(), 因此还会调用最上面的 func() 函数

# 上面的timmer就是装饰器
# 但是这里还是多了一行代码,比如有另一个 func1(), 那么还得再加一行:func1 = timmer(func1)
# Python解释器提供了一个语法糖 @ 的概念

import time
def timmer(f):
    def inner():
        start_time = time.time()
        f()
        end_time = time.time()
        print("该函数的执行效率是%s" % (end_time - start_time))
    return inner

# # 前提是先定义一个 timmer 函数
@timmer  # 相当于 func =  timmer(func) ,要记住这里的 timmer(func) 是 inner 函数
def func():
    print("测试这个函数的执行效率...")
    time.sleep(0.2)
func()

@timmer
def func1():
    print(666)
    time.sleep(0.2)
func1()
# 这还有个问题,被装饰的函数 func、func1 如果有参数,这样写就满足不了了
# 比如下面这样

import time
def timmer(f):
    def inner():
        start_time = time.time()
        f()
        end_time = time.time()
        print("该函数的执行效率是%s" % (end_time - start_time))
    return inner

@timmer
def func(x):
    print("测试一个参数:%s" %x)
    time.sleep(0.2)
func("abc")  # 会报错
# 因为这里的 func("abc") 相当于 inner("abc"),而 inner() 是没有参数的
# 因此要在 inner()中传入一个参数

import time
def timmer(f):
    def inner(a):
        start_time = time.time()
        f(a)
        end_time = time.time()
        print("该函数的执行效率是%s" % (end_time - start_time))
    return inner

@timmer
def func(x):
    print("测试一个参数:%s" %x)
    time.sleep(0.2)
func("abc")

# 这个功能还是不完善,如果有两个参数以上,inner() 及 inner 函数里的 f() 里面就得加入两个参数
# 这时就要想到之前学过的动态参数(万能参数)
import time
def timmer(f):
    def inner(*args, **kwargs):  # 函数的定义,* 代表聚合
        start_time = time.time()
        f(*args, **kwargs)
        # 比如这里 func1 中的两个万能参数为什么可以平均合理分配给 func 函数中 x, y, z 三个参数?
        # 因为 f(*args, **kwargs)是调用函数,而括号中的 *args **kwargs 就是打散
        # 所以这里的 f(*args, **kwargs) 相当于 f("abc", "jjk", "hkas")
        end_time = time.time()
        print("该函数的执行效率是%s" % (end_time - start_time))
    return inner

@timmer
def func(x):
    print("测试一个参数:%s" %x)
    time.sleep(0.2)
func("abc")

@timmer
def func1(x, y, z="alex"):
    print("测试两个参数:%s" % x, y)
    print("测试一个参数:%s" % z)
    time.sleep(0.2)
func("abc", "jjk", "hkas")

# 这里还差一点,被装饰函数缺少返回值
# 函数有返回值 return 时

import time
def timmer(f):
    def inner(*args, **kwargs):
        start_time = time.time()
        f(*args, **kwargs)
        end_time = time.time()
        print("该函数的执行效率是%s" % (end_time - start_time))
    return inner

@timmer
def func(x):
    print("测试一个参数:%s" %x)
    time.sleep(0.2)
    return 666
func("abc")
# 运行发现没有返回 666,因为 inner() 中没有返回值

import time
def timmer(f):
    def inner(*args, **kwargs):
        start_time = time.time()
        ret = f(*args, **kwargs)  # 注意这里
        end_time = time.time()
        print("该函数的执行效率是%s" % (end_time - start_time))
        return ret  # 借助 inner() 函数设置一个返回值
    return inner

@timmer
def func(x):
    print("测试一个参数:%s" %x)
    time.sleep(0.2)
    return 666
s = func("abc")
print(s)

# 这里发现去掉 @timmer 也一样能打印出 666
# 标准版装饰器写法

def wrapper(f):
    def inner(*args, **kwargs):
        """执行被装饰函数之前的操作"""
        ret = f(*args, **kwargs)
        """执行被装饰函数之后的操作"""
        return ret
    return inner

# 装饰器:
#   1. 它的本质是闭包,而且它就是个函数
#   2. 在不改变原函数调用方式上,给原函数增加了一些额外的功能,比如登录验证,写日志,执行效率等

# 开放封闭原则:
#   对代码拓展开放——任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。
#                  所以我们必须允许代码扩展、添加新功能
#   对代码修改封闭——因为写的函数,很有可能已经交付给其他人使用了,如果这个时候我们对其进行了修改,
#                  很有可能影响其他已经在使用该函数的用户。

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

# 比如上一段代码中的 func() 函数如果还要加一个功能
# 不要在这个被装饰函数里改,只需在装饰器 timmer 里的 inner 函数添加即可

猜你喜欢

转载自www.cnblogs.com/shawnhuang/p/10219768.html
今日推荐