什么是高阶函数?

-- 把函数名当做参数传给另外一个函数,在另外一个函数中通过参数调用执行

#!/usr/bin/python3

__author__ = 'beimenchuixue'
__blog__ = 'http://www.cnblogs.com/2bjiujiu/' def func_x(x):
return x * 2 def func_y(y):
return y * 3 def func_z(x, y):
# 等价于 return func_x(5) + func_y(3)
return x(5) + y(3) if __name__ == '__main__':
# 把函数当做参数,本质上是把函数的内存地址当做参数传递过去,
result = func_z(func_x, func_y)
print(result)

什么是装饰器?

  -- 在不改变源代码的基础上扩展新需求,装饰器本身也是函数,应用高阶函数实现

  -- 把被装饰的函数内存地址当参数传入装饰器函数体,通过参数调用被装饰的函数

装饰器原则:

    -- 不改变源代码                         - 因为函数可能在其他地方各种调用,一改动全身

    -- 不改变原函数调用顺序           - 源代码有自己的逻辑处理

    -- 装饰器又叫做语法糖

装饰器逻辑上格式?

- 高阶函数+嵌套函数

    需求:

      给某个函数增加一个计算运行时间功能

#!/usr/bin/python3

__author__ = 'beimenchuixue'
__blog__ = 'http://www.cnblogs.com/2bjiujiu/' import time def total_time(func): # func = hell_word
def wrapper(): # 等价于hell_word()
start_time =time.time()
func()
end_time = time.time()
print(end_time - start_time) # 打印统计时间
return wrapper # 通过装饰器给hell_word函数装上了统计时间的功能,功能逻辑在装饰器中实现
@total_time
def hell_word():
time.sleep(0.5)
print('hello word') if __name__ == '__main__':
hell_word()

       相当于下面的函数逻辑

#!/usr/bin/python3

__author__ = 'beimenchuixue'
__blog__ = 'http://www.cnblogs.com/2bjiujiu/' import time

# 装饰器函数
def total_time(func):
def wrapper():
start_time =time.time()
func()
end_time = time.time()
print(end_time - start_time)
return wrapper

def hell_word():
time.sleep(0.5)
print('hello word') if __name__ == '__main__':
# 把函数当做参数传入装饰器函数,然后装饰器函数返回包裹函数wrapper地址,执行装饰器函数本质上执行包裹函数wrapper中逻辑
total_time(hell_word)()

假如传入的函数中有参数如何?

  -- 需要在wrapper和func中加入收集参数(*args)或收集字典参数(**kwargs),

  -- warps和func可自定义名字,默认如此命名

如果原函数有个返回值,该如何?

  -- 如果到func结束 直接在func()前面加return

  -- 到func未结束,可以func()结果赋值给一个变量,res = func(*args,**kwargs)到新增逻辑结束后加上 return res

需求:

  计算出斐波那契数列中第n个数的值?

  求一个共有10个台阶的楼梯,从下走到上面,一次只能迈出1~3个台阶,并且不能后退,有多少中方法?

  要求:

     通过装饰器实现剪枝函数

如何逻辑整理这个需求? 

  斐波那契数列(黄金分割数列),从数列的第3项开始,每一项都等于前两项之和

  每次迈出都是 1~3 个台阶,剩下就是 7~9 个台阶

如果迈出1个台阶,需要求出后面9个台阶的走法

如果迈出2个台阶,需要求出后面8个台阶的走法

如果迈出3个台阶,需要求出后面7个台阶的走法

此3种方式走法,通过递归方式实现,递归像树,每次递归都生成子节点函数

#!/usr/bin/python3

__author__ = 'beimenchuixue'
__blog__ = 'http://www.cnblogs.com/2bjiujiu/' def jian_zhi(func):
# 中间字典,判断已经是否求解过
median = {} def wrapper(*args):
# 假如不在中间字典中,说明没有求解过,添加到字典中去,在的话,直接返回, 将不在递归下去,保证每次递归的唯一性
if args not in median:
median[args] = func(*args)
return median[args] return wrapper @jian_zhi
def fibonacci(n):
if n <= 1:
return 1
return fibonacci(n - 1) + fibonacci(n - 2) @jian_zhi
def climb(n, steps):
count = 0
# 当最后台阶为0的时候,说明最后只是走了一次
if n == 0:
count = 1
# 当最后台阶不为0的时候,说明还需要走至少一次
elif n > 0:
# 对三种情况进行分别处理momo
for step in steps:
count += climb(n - step, steps) # 返回每次递归的计数
return count if __name__ == '__main__':
print(climb(10, (1, 2, 3)))
print(fibonacci(20))

需求:

  实现在装饰器函数中,保留 被装饰函数 的元数据

那,什么是函数的元数据?

在函数对象中保存着一些函数的元数据,如:

f.__name__           函数名

f.__doc__              函数文档

f.__moudle__       函数所属模块名

f.__dict__              属性字典

f.__defaults__       默认参数组

……

在使用装饰器后,在装饰器里访问以上属性时,我们看到的是装饰器函数的元数据

那,如何解决这个需求?

  通过 functools中的wraps或update_wrapper方法实现,其中每个方法都可单独实现

#!/usr/bin/python3

__author__ = 'beimenchuixue'
__blog__ = 'http://www.cnblogs.com/2bjiujiu/' import time
from functools import (wraps, update_wrapper, WRAPPER_ASSIGNMENTS, WRAPPER_UPDATES) def count_time(func):
"""
给目标函数加上计算运行时间统计
""" # 这个装上器和update_wrapper一样,默认参数WRAPPER_ASSIGNMENTS, WRAPPER_UPDATES
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time() # 定义result接收函数返回值,并且在装饰函数最后返回回去
resutl = func(*args, **kwargs)
print('运行时间:', time.time() - start_time)
return resutl # 其中默认参数 WRAPPER_ASSIGNMENTS, WRAPPER_UPDATES
# update_wrapper(wrapper, func)
return wrapper @count_time
def add(num=100):
"""
计算 0~num 累加值,默认num=100
"""
time.sleep(1)
return sum([x for x in range(num + 1)]) if __name__ == '__main__':
print('函数名:', add.__name__)
print('属性字典:', add.__dict__)
print('函数默认参数:', add.__defaults__)
print('函数所在模块:', add.__module__)
print('函数文档:', add.__doc__) # 打印两个默认参数
# WRAPPER_ASSIGNMENTS :__module__', '__name__', '__qualname__', '__doc__', '__annotations__
# WRAPPER_UPDATES:__dict__
print(WRAPPER_ASSIGNMENTS, WRAPPER_UPDATES)
result = add()
print(result)

  

需求:

  实现一个装饰器,用它来检查被装饰函数的参数类型,装饰器可以通过函数,指明函数参数类型,进行函数调用的时候,传入参数,检测到不匹配时,抛出异常

那,如何解决这个需求?

  -- 先要获取函数的签名,并且获得装饰器中参数,然后把函数签名和装饰器中参数对应绑定

  -- 把调用函数时候传入的参数和函数签名进行绑定

  -- 把实参和装饰器中定义的数据进行类型比较,不匹配抛出异常

#!/usr/bin/python3

__author__ = 'beimenchuixue'
__blog__ = 'http://www.cnblogs.com/2bjiujiu/' from inspect import signature def check_type(*ty_args, **ty_kwargs):
def out_wrapper(func):
# 通过signature方法,获取函数形参:name, age, height
sig = signature(func)
# 获得装饰器传来的参数, 函数签名与之绑定,字典类型
bind_types = sig.bind_partial(*ty_args, **ty_kwargs).arguments
print(bind_types) def wrapper(*args, **kwargs):
# 给执行函数中具体的实参进行和形参进行绑定,形成字典的形式
func_type = sig.bind(*args, **kwargs).arguments.items()
print(func_type)
# 循环形参和实参字典的items()形式
for name, obj in func_type:
if name in bind_types:
# 判断实参是否是指定类型数据
if not isinstance(obj, bind_types[name]):
raise TypeError('%s must be %s' % (name, bind_types[name]))
# 假如函数有返回值,通过此方法返回函数的返回值
res = func(*args, **kwargs)
return res return wrapper return out_wrapper # 通过装饰器实现对函数参数进行类型检查
@check_type(str, int, float)
def func(name, age, height):
print(name, age, height) if __name__ == '__main__':
# 正常数据
func('bei_men', 18, 1.75)
# 错误数据
func('bei_men', '18', 1.75)

  

案例:

为分析程序内哪些函数执行时间开销较大,我们需定义一个带timeout参数的装饰器

需求:

    统计被装饰函数的运行时间

    时间大于timeout时,将此次函数调用记录到log日志中

    运行时可以修改timeout的值

如何解决这个问题?

  1. 定义一个装饰器,计算函数执行时间,并与timeout比较,当大于timeout时候,通过logging模块打印出日志信息
  2. 在包裹函数中添加一个函数,通过这个函数来修改timeout变量
  3. 在python3中用nonlocal来声明嵌套作用域中的变量引用,在python2中可以通过把timeout参数变成列表,通过列表索引来进行改值
    #!/usr/bin/python3
    
    __author__ = 'beimenchuixue'
    __blog__ = 'http://www.cnblogs.com/2bjiujiu/' import time
    import logging
    from random import randint def run_time(timeout):
    """
    定义检查函数运行时间,并打印对应函数运行时间超出设定时间日志,并支持更改timeout
    """ # python2
    # timeout = [timeout] # 真正包裹函数
    def out_wrapper(func):
    def wrapper(*args, **kwargs):
    start_time = time.time()
    result = func(*args, **kwargs)
    used_time = time.time() - start_time # 对于超出timeout的函数进行日志打印
    if used_time > timeout:
    msg = '%s: %s > %s' % (func.__name__, used_time, timeout)
    logging.warn(msg) # python2
    # if used_time > timeout[0]:
    # msg = '%s: %s > %s' % (func.__name__, used_time, timeout[0])
    # logging.warn(msg)
    # return result
    return result # 设置timeout参数值
    def set_timeout(value):
    # 声明嵌套域变量,可以更改,python2通过把列表形式进行更改
    nonlocal timeout
    timeout = value # 定义接口
    wrapper.set_timeout = set_timeout # python2
    # def set_timeout(value):
    # timeout[0] = value
    # wrapper.set_timeout = set_timeout return wrapper return out_wrapper @run_time(1.5)
    def func():
    # 随机有50%的几率程序沉睡1秒
    while randint(0, 1):
    time.sleep(1)
    print('func_run') if __name__ == "__main__":
    for _ in range(10):
    func() print('_' * 50) # 更改run_time装饰器中timeout参数
    func.set_timeout(2)
    for _ in range(10):
    func()

      

如何逻辑整理?

  -- 3层 :一层获得value,二层偷梁换柱,三层逻辑处理

  -- 2层:一层偷梁换柱,二层逻辑处理

python_语法糖_装饰器的更多相关文章

  1. Python基础之函数:3、多层语法糖、装饰器和装饰器修复技术及递归函数

    目录 一.多层语法糖 1.什么是多层语法糖: 2.多层语法糖用法: 二.有参装饰器 1.什么是有参装饰器: 2.有参装饰器的作用: 三.装饰器修复技术 1.什么是装饰器修复技术: 四.递归函数 1.什 ...

  2. python3.5-day5_迭代器_生成器_装饰器_模块

    笔者QQ 360212316 迭代器&生成器 生成器: 一个函数调用返回一个迭代器,那这个函数叫做生成器,如果函数中包含yield语法,那么这个函数就会变成生成器 生成器的特点: 1.生成器必 ...

  3. python基础语法_9-1闭包 装饰器补充

    1.闭包的概念 closure:内部函数中对enclosing作用域的变量进行引用,外部函数返回内部函数名   2.函数实质与属性 函数是一个对象:在内存中有一个存储空间 函数执行完成后内部变量回收: ...

  4. python语法_装饰器

    装饰器的知识点储备: 1 作用域 2 高阶函数 3 闭包 内部函数,对外部作用作用域的变量进行了引用,该内部函数就认为是闭包, def outer(): x=10 def inner(): print ...

  5. Python成长之路_装饰器

    一.初入装饰器 1.首先呢我们有这么一段代码,这段代码假设是N个业务部门的函数 def f1(aaa): print('我是F1业务') if aaa == 'f1': return 'ok' def ...

  6. python基础语法8 叠加装饰器,有参装饰器,wraps补充,迭代器

    叠加装饰器: 叠加装饰器 - 每一个新的功能都应该写一个新的装饰器 - 否则会导致,代码冗余,结构不清晰,可扩展性差 在同一个被装饰对象中,添加多个装饰器,并执行. @装饰1 @装饰2 @装饰3 de ...

  7. python基础16_闭包_装饰器

    不了解是否其他语言也有类似 python 装饰器这样的东西. 最近才发现ECMAScript6也是有生成器函数的,也有 yield  generator 装饰器的基础知识是闭包: # 闭包:嵌套函数, ...

  8. 20181122_C#中AOP初探_装饰器模式的AOP_Remoting实现AOP_Castle实现AOP

    一.   什么是AOP: a)         AOP是面向切面编程; 就像oop一样, 它也是一种编程思想; i.    Oop思想→一切皆对象, 对象交互组成功能, 功能叠加组成模块, 模块叠加组 ...

  9. Python练习_装饰器、生成器_day12

    装饰器 装饰器篇: 1.编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码. def login(func): def inner( ...

随机推荐

  1. Intellij Idea中Backspace无法使用,Ctrl+c/Ctrl+d等等快捷键无法使用的问题的解决

    1:作为一个强迫症的我使用习惯了Eclipse,可能是对快捷键的依赖性,都说Idea是开发Java的最好工具,下载,安装等等(过程省略,百度很多方法),这里说一下我遇到的窘迫问题.哎,真是有时候看似天 ...

  2. 在地铁上看了zabbix 的书发现 "报警执行远程命令"

    在地铁上看了zabbix 的书发现 "报警执行远程命令" 远程命令整个过程:  items -> triggers -> action -> remote com ...

  3. api接口写好了?想过(Accept,Content-Type)?返回类型json|xml?

    api接口写好了?想过(Accept,Content-Type)?返回类型json|xml? 起因: - A,B. A调用B提供的api接口. - A:为毛你的接口返回的是xml格式的(浏览器访问)? ...

  4. 找出生成json中的error_code,并加以处理

    需求: 前段时间调用了百度AI的分词接口,因为不完全支持并发,一些调用产生了错误,混在json内部. 现在需要将未调用成功的内容重新调用一遍. 思考过程: 方法一: 开始想到的是调用的过程当中,如果报 ...

  5. web.xml解析

    常用元素及含义 <!-- standalone 定义了外部定义的 DTD 文件的存在性,有效值是 yes和 no --> <?xml version="1.0" ...

  6. php中的echo,json_decode,json_encode常用函数使用注意事项

    ---恢复内容开始--- 1.echo函数 echo只能输出单个字符串或者整数,不能直接输出数组.要输出多个字符串必须用分号 eg: echo可以输出字符串加变量,如果输出的数字字符串则会将对应的数字 ...

  7. 透过一道面试题来探探JavaScript中执行上下文和变量对象的底

    在做面试题之前,我们先搞清楚两个概念 执行上下文(execution context) 变量对象(variable object) 执行上下文 我们都知道JavaScript的作用域一共分三种 全局作 ...

  8. Messagepack原理

    什么是Messagepack? 用官方的话说:MessagePack是一种高效的二进制序列化格式.它允许您像JSON一样在多个语言之间交换数据.但是,它更快并且更小.小整数被编码为一个字节,和典型的短 ...

  9. 关于Springmvc中include与Sitemesh装饰器的基本使用

    关于Springmvc中include与Sitemesh装饰器的使用 !!!转载请注明出处=>http://www.cnblogs.com/funnyzpc/p/7283443.html 静态包 ...

  10. 【JavaScript for循环实例】

    1.大马驮2石粮食,中马驮1石粮食,两头小马驮一石粮食,要用100匹马,驮100石粮食,该如何调配? //驮100石粮食,大马需要50匹 for(var a=0;a<=50;a++){ //驮1 ...