当前位置: 首页 > news >正文

python——装饰器深入研究(二)

在这里插入图片描述

一、装饰器的副作用

函数名称.__name__:获取函数名称
函数名称.__doc__:获取函数注释

def work1():
    """
    函数的文档字符串注释
    :return:
    """
    print("函数--work1---")

print('函数名称:',work1.__name__)
print('函数文档注释:',work1.__doc__)

执行结果:当函数没有被装饰器装饰时的执行结果为:

函数名称: work1
函数文档注释: 
    函数的文档字符串注释
    :return:
    

二、函数被装饰器装饰后

def decorator(func):

    def wrapper(*args,**kwargs):

        res=func(*args,**kwargs)

    return wrapper



@decorator
def work1():
    """
    函数的文档字符串注释
    :return:
    """
    print("函数--work1---")

print('函数名称:',work1.__name__)
print('函数文档注释:',work1.__doc__)

执行结果:函数被装饰器装饰后的执行结果

函数名称: wrapper
函数文档注释: None

三、副作用:

由于装饰器装饰了之后,原函数名字指向的是装饰器内部的闭包,
因此会产生副作用,无法在通过函数名,去正常获取原函数的属性

四、副作用消除

4.1、wraps的作用:获取装饰器中所传的函数的属性,并且把属性给被装饰器装饰的函数中

def user():
    """用户函数"""

@wraps(user)
def login():
    '''定义函数'''
    print('登录')
print("login函数的名字:",login.__name__)
print("login函数的文档注释:",login.__doc__)

login()

执行结果:

login函数的名字: user
login函数的文档注释: 用户函数
登录

4.2、从4.1函数为例进行源码分析:

wraps源码:

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

       Returns a decorator that invokes update_wrapper() with the decorated
       function as the wrapper argument and the arguments to wraps() as the
       remaining arguments. Default arguments are as for update_wrapper().
       This is a convenience function to simplify applying partial() to
       update_wrapper().
    """
    return partial(update_wrapper, wrapped=wrapped,
                   assigned=assigned, updated=updated)

update_wrapper()源码

WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',
                       '__annotations__')
WRAPPER_UPDATES = ('__dict__',)
def update_wrapper(wrapper,
                   wrapped,
                   assigned = WRAPPER_ASSIGNMENTS,
                   updated = WRAPPER_UPDATES):
    """Update a wrapper function to look like the wrapped function

       wrapper is the function to be updated
       wrapped is the original function
       assigned is a tuple naming the attributes assigned directly
       from the wrapped function to the wrapper function (defaults to
       functools.WRAPPER_ASSIGNMENTS)
       updated is a tuple naming the attributes of the wrapper that
       are updated with the corresponding attribute from the wrapped
       function (defaults to functools.WRAPPER_UPDATES)
    """
    for attr in assigned:
        try:
            value = getattr(wrapped, attr)
        except AttributeError:
            pass
        else:
            setattr(wrapper, attr, value)
    for attr in updated:
        getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
    # Issue #17482: set __wrapped__ last so we don't inadvertently copy it
    # from the wrapped function when updating __dict__
    wrapper.__wrapped__ = wrapped
    # Return the wrapper so this can be used as a decorator via partial()
    return wrapper

解析图

在这里插入图片描述
相当于把user属性一个一个的拿出来添加到login函数中

4.3、消除副作用

@wraps(func) :将func(work1)函数的属性复制一份给wrapper

from functools import wraps
def decorator(func):

    @wraps(func)                    #将func(work1)函数的属性复制一份给wrapper
    def wrapper(*args,**kwargs):

        res=func(*args,**kwargs)

    return wrapper



@decorator
def work1():
    """
    函数的文档字符串注释
    :return:
    """
    print("函数--work1---")

print('函数名称:',work1.__name__)
print('函数文档注释:',work1.__doc__)

执行结果

函数名称: work1
函数文档注释:
函数的文档字符串注释
:return:

五、通过装饰器对函数和类进行属性添加和修改

5.1、给函数添加属性

函数名称.属性名称=‘属性值’
函数名称.__dict__:获取函数所有的属性和方法

def work1():
    """
    函数的文档字符串注释
    :return:
    """
    print("函数--work1---")

#函数对象的属性
print('函数名称:',work1.__dict__)
#print(type(work1))

work1.desc='给函数添加属性'
work1.age=18
print('函数名称:',work1.__dict__)

函数名称: {}
函数名称: {‘desc’: ‘给函数添加属性’, ‘age’: 18}

5.2、通过装饰器给函数添加属性

特别注意:

闭包形式的装饰器一般用于:拓展功能
普通函数作为装饰器:一般用于对函数和类的属性进行修改添加,案例如下

def add_attr(func):
    func.age=1
    func.data=[2,8,23,22,24]

    return func

@add_attr           #todo work1=add_attr(work1)
def work1():
    """
    函数的文档字符串注释
    :return:
    """
    print("函数--work1---")

#函数对象的属性
print('函数名称:',work1.__dict__)

执行结果:
函数名称: {‘age’: 1, ‘data’: [2, 8, 23, 22, 24]}

执行逻辑:

1、@add_attr :将函数work1作为参数传递给装饰器add_attr;
2、并且将add_attr(work1)用与函数同名的变量work1来接收,即work1=add_attr(work1)
3、调用work1()即执行func
4、装饰器内部实现的功能给函数work1添加属性age、data

5.3、通过装饰器给类添加属性

def add_attr(func):
    func.age=1
    func.data=[2,8,23,22,24]

    return func

@add_attr
class Demo:
    pass

print(Demo.__dict__)

执行结果:新增了属性:‘age’: 1, ‘data’: [2, 8, 23, 22, 24]
{‘module’: ‘main’, ‘dict’: <attribute ‘dict’ of ‘Demo’ objects>, ‘weakref’: <attribute ‘weakref’ of ‘Demo’ objects>, ‘doc’: None, ‘age’: 1, ‘data’: [2, 8, 23, 22, 24]}

在这里插入图片描述

相关文章:

  • 前端面试谈:简历通用注意事项
  • Inveigh结合DNS v6配合NTLM Relay 的利用
  • Vue学习之--------路由的query、params参数、路由命名(3)(2022/9/5)
  • 华为交换机配置ACL
  • 离职总结(2022-9-5)
  • 计算机组成原理_Cache的替换算法
  • (附源码)springboot 校园学生兼职系统 毕业设计 742122
  • 如何快速提取设计地形等高线?
  • 详解MeerEVM:MeerDAG共识下的智能合约执行引擎
  • 上万字全面解读websocket(多种实现方案,含集群实现代码)
  • 【网络安全】XSS跨站脚本攻击专题讲解
  • 【栈和队列OJ】一、有效的括号
  • 行业竞争分析及发展动向
  • c++现代特性
  • 【学习笔记】go协程和通道
  • 【干货分享】SpringCloud微服务架构分布式组件如何共享session对象
  • 30秒的PHP代码片段(1)数组 - Array
  • Apache Pulsar 2.1 重磅发布
  • ES6系列(二)变量的解构赋值
  • GDB 调试 Mysql 实战(三)优先队列排序算法中的行记录长度统计是怎么来的(上)...
  • JAVA_NIO系列——Channel和Buffer详解
  • js
  • Kibana配置logstash,报表一体化
  • nodejs实现webservice问题总结
  • node学习系列之简单文件上传
  • TCP拥塞控制
  • 阿里云购买磁盘后挂载
  • 翻译:Hystrix - How To Use
  • 高度不固定时垂直居中
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 前端之React实战:创建跨平台的项目架构
  • 项目实战-Api的解决方案
  • 小程序滚动组件,左边导航栏与右边内容联动效果实现
  • 中国人寿如何基于容器搭建金融PaaS云平台
  • ​ssh-keyscan命令--Linux命令应用大词典729个命令解读
  • #中国IT界的第一本漂流日记 传递IT正能量# 【分享得“IT漂友”勋章】
  • (AngularJS)Angular 控制器之间通信初探
  • (C语言)二分查找 超详细
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (算法二)滑动窗口
  • (转)程序员技术练级攻略
  • (转)树状数组
  • **Java有哪些悲观锁的实现_乐观锁、悲观锁、Redis分布式锁和Zookeeper分布式锁的实现以及流程原理...
  • .NET Core 2.1路线图
  • .NET Core WebAPI中使用swagger版本控制,添加注释
  • .net oracle 连接超时_Mysql连接数据库异常汇总【必收藏】
  • .Net 应用中使用dot trace进行性能诊断
  • .net 怎么循环得到数组里的值_关于js数组
  • 。Net下Windows服务程序开发疑惑
  • @Import注解详解
  • [ Linux 长征路第二篇] 基本指令head,tail,date,cal,find,grep,zip,tar,bc,unname
  • [.NET]桃源网络硬盘 v7.4
  • []串口通信 零星笔记
  • [202209]mysql8.0 双主集群搭建 亲测可用
  • [28期] lamp兄弟连28期学员手册,请大家务必看一下