python_装饰器_语法糖

什么是高阶函数?

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

#!/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()
    

案例:

实现一个能将函数调用信息记录到日志的装饰器

 需求:

  1. 把每次函数的调用时间,执行时间,调用次数写入日志
  2. 可以对被装饰函数分组,调用信息记录到不同日志
  3. 动态修改参数,比如日志格式
  4. 动态打开关闭日志输出功能

如何解决这个问题?

  为了装饰器的灵活性,定义一个装饰类,把这个类的实例方法当做装饰器,在类中装饰器方法持有实例对象,便于修改属性和扩展功能

#!/usr/bin/python3

__author__ = ‘beimenchuixue‘
__blog__ = ‘http://www.cnblogs.com/2bjiujiu/‘

import logging
from time import time, strftime, localtime, sleep
from random import choice
from functools import wraps

class ToLog():
    def __init__(self, name):
        log = logging.getLogger(name)
        log.setLevel(logging.INFO)
        # 日志保存文件名字
        file_name = logging.FileHandler(name + ‘.log‘)
        # 添加日志文件
        log.addHandler(file_name)
        # 日志格式
        log.info(‘start‘.center(50, ‘-‘))
        self.log = log
        self.temp = ‘%(func)s -> [%(start_time)s - %(used_time)s - %(naclls)s]‘

    def go_log(self, func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            # 函数每调用一次加1
            wrapper.naclls += 1

            start_time = time()
            res = func(*args, **kwargs)
            used_time = time() - start_time

            info = {}
            info[‘func‘] = func.__name__
            info[‘start_time‘] = start_time
            info[‘used_time‘] = used_time
            info[‘naclls‘] = wrapper.naclls

            msg = self.temp % info
            # 把日志按格式写入文件
            self.log.info(msg)

            return res

        # 初始化调用次数参数
        wrapper.naclls = 0

        return wrapper

    # 重新定义日志记录模版
    def set_log_temp(self, temp):
        self.temp = temp

    # 关闭日志功能
    def log_off(self):
        self.log.setLevel(logging.WARN)

    # 打开日志功能
    def log_on(self):
        self.log.setLevel(logging.INFO)

# 实例化出两个装饰器对象
log_one = ToLog(‘one‘)
log_two = ToLog(‘two‘)

# 修改实例2的日志模版,去掉执行时间
log_two.set_log_temp(‘%(func)s -> [%(start_time)s - %(naclls)s]‘)

# 关闭log_two中记录日志功能
log_two.log_off()

@log_one.go_log
def func_one():
    print(‘one‘)

@log_one.go_log
def func_two():
    print(‘two‘)

@log_two.go_log
def func_three():
    print(‘three‘)

if __name__ == ‘__main__‘:
    for _ in range(50):
        choice([func_one, func_two, func_three])()
        sleep(choice([0.5, 1, 1.5]))

如何逻辑整理?

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

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

时间: 2024-12-31 05:44:01

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

闭包函数、装饰器以及语法糖

闭包函数: 1.闭包函数必须在函数内部定义 2.闭包函数可以引用外层函数的名字 闭包函数是 函数嵌套.函数对象.名称空间与作用域 结合体. # 直接传参 def func(x): print(x) func(1000) # 通过闭包函数传参 def outer(number): # number = 100 # inner就是闭包函数 def inner(): print(number) return inner func = outer(1000) # ---> inner地址 ---> f

python学习笔记之装饰器(语法糖)

什么是装饰器 装饰器的知识点铺垫(函数即变量,高阶函数,嵌套函数) 不带参数的装饰器示例 带参数的装饰器示例 作业 一.什么是装饰器 本质上,装饰器就是返回一个函数的高阶函数.装饰器就是一个函数 装饰器的原则: 不修改被装饰对象的源代码 不修改被装饰对象的调用方式 二.装饰器涉及的知识点 函数即变量 高阶函数 嵌套函数 函数即变量: 在python中,一个变量首先被定义,分配内存空间,然后再使用. 以x=1,这个简单的赋值语句为例子.首先在内存中分配一个空间,x指向该内存空间,该内存空间内存入"

python_装饰器

装饰器的语法结构: 使用关键字 @. #高阶函数 加上闭包返回值:高阶函数就是不在原函数体上数据和调用结果,添加其他的功能 def timmer(func): def warpper(): start_time = time.time() #开始时间 res = func() #这个的函数是调用的test() stop_time = time.time() #结束时间 print('查看我运行的时间是:%s' %(stop_time-start_time)) return res return

Python_装饰器,生成器_干货哦

import timeimport calendarimport os # 时间格式熟悉"""# 格式化成2016-03-20 11:45:39形式time01 = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())print()# 格式化成Sat Mar 28 22:24:24 2016形式print(time.strftime("%a %b %d %H:%M:%S %Y", ti

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

笔者QQ 360212316 迭代器&生成器 生成器: 一个函数调用返回一个迭代器,那这个函数叫做生成器,如果函数中包含yield语法,那么这个函数就会变成生成器 生成器的特点: 1.生成器必须从前往后一次访问,不能跳着取 2.生成器只记录访问的数据,其他数据释放 3.生成器的作用就是预想可能用到那么多的数据 生成器的两种方式: 方式一: data = (x * 2 for x in range(5)) # 获取结果的两种方式 print(data.__next__()) print(next(

python_装饰器模式

装饰器模式定义:动态地给一个对象添加一些额外的职责. 在Python中Decorator mode可以按照像其它编程语言如C++, Java等的样子来实现,但是Python在应用装饰概念方面的能力上远不止于此,Python提供了一个语法和一个编程特性来加强这方面的功能. 首先需要了解一下Python中闭包的概念:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure). def makeblod(fn): def wrapped():

react项目的中使用mobx状态管理安装ES7装饰器(Decorator)语法教程

那么如何实现装饰器的使用呢? 1.在命令行工具中使用 npm run eject.不熟的情况下可能会报错,如果报错的信息大概意思是:有些文件未被追踪到,那么直接git add . 再 git commit -m "",或者直接在.gitignore中忽略这些文件(不建议)2.npm run eject之后package.json中会出现很多依赖建议yarn/npm i 一下.3.然后打开package.json文件,找到“babel”开头的一个对象,(一般在最后,),这是原始的样子:

python3_装饰器_异常处理

装饰器: def auth(func):     def wrapper(name):                     如果函数带参数,要加在这里         user=raw_input("input passwd:").strip()         if user=='test':             print "welcome login"             func(name)              如果函数带参数,要加在这里

python_20_装饰器_高阶函数的使用_01

登陆验证代码. 1 #用户验证功能,不修改源代码,使用装饰器. 2 user_dic={'username':None,'login':False} #定义全局变量 3 4 def auth_func(func): 5 def wrapper(*args,**kwargs): 6 if user_dic['username'] and user_dic['login']: 7 res = func(*args, **kwargs) 8 return res 9 user_name = input