Python3-Cookbook(第九章) - 元编程Part1

一、函数装饰器入门

    对应Cookbook章节 9.1、9.2、9.3 和9.8

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2023/8/4 18:44
# @Author  : Maple
# @File    : 01-函数装饰器入门.py

#  Cookbook 9.1、9.2、9.3 和9.8


import time
from functools import wraps

def timethis(func):
    '''
    Decorator that reports the execution time.
    '''
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(func.__name__, end-start)
        return result
    return wrapper

@timethis
def countdown(n):
    '''
    Counts down
    '''
    print('hello')
    while n > 0:
      n -= 1

# 多层装饰器
def decorator1(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print('Decorator 1')
        return func(*args, **kwargs)
    return wrapper

def decorator2(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print('Decorator 2')
        return func(*args, **kwargs)
    return wrapper

@decorator1
@decorator2
def add(x, y):
    return x + y

# @wraps装饰func的底层 到底发生了什么?
# Part1
"""
# 1.wrapped是被装饰的函数
# 2.WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__','__annotations__')
# 3.WRAPPER_UPDATES = ('__dict__',)
# 4.返回一个 update_wrapper 的偏函数

def wraps(wrapped,
          assigned = WRAPPER_ASSIGNMENTS,
          updated = WRAPPER_UPDATES):
    Decorator factory to apply update_wrapper() to a wrapper function
 
    return partial(update_wrapper, wrapped=wrapped,
                   assigned=assigned, updated=updated)

"""

# Part2
"""
# 1. wrapper是装饰器被装饰后 返回的函数  
# 2. updated =  ('__dict__',)

def update_wrapper(wrapper,
                   wrapped,
                   assigned = WRAPPER_ASSIGNMENTS,
                   updated = WRAPPER_UPDATES):
    Update a wrapper function to look like the wrapped function

    
    for attr in assigned:
        try:
            # 1. 取出`被装饰`函数的属性
            value = getattr(wrapped, attr)
        except AttributeError:
            pass
        else:
            # 2.将第一步取出来的属性值 赋值给 wrapper,也就是说wrapper的元数据实际上替换成[被装饰的函数]了
            #    以此实现了被装饰函数 元数据的保留
            setattr(wrapper, attr, value)
    for attr in updated:
        # 1.update方法实现 字典值的更新,注意是 后面更新前面的(逻辑: 交叉的key,用后面的值更新前面的; 只存在于后面的key,添加到前面的字典 )
        # 2.如果wrapped 有 ('__dict__',)属性[Tips个人理解:类才有dict属性,方法没有这个属性],那么就把wrapper的dict属性也替换成wrapped的
        # 3.如果wrapped 没有 dict属性, getattr去默认值{},而空字典 对wrapper.__dict__'的值 不会有任何影响
        getattr(wrapper, attr).update(getattr(wrapped, attr, {}))

    return wrapper

"""



if __name__ == '__main__':

    # 1. 简单装饰器测试
    countdown(1000000)
    print('===================')



    # 2.解除装饰器
    # 2-1 方法之一
    """
    1.适用场景: 在函数已经有装饰器的情况下,但是又要单独测试函数本身的场景
    2.必要条件1: 有@wraps修饰
    2.必要条件2:只有一个装饰器,多个装饰器的情况解除之后的测试结果不可预测
    """
    # 通过__wrapped__直接访问被包装的函数:即countdown自身(不会走装饰器里面的逻辑)
    countdown.__wrapped__
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值