notebooks
  • notebooks
  • _planning
    • 2022 OKR
    • basketball
    • swimming
  • communication
    • Dubbo
    • Kafka
    • Messaging
    • RPC
    • Thrift
  • computation
    • map-reduce
  • cs-basic-knowledge
    • computer-architecture
    • data-structure-and-algorithms
    • networks
    • os
  • devops
    • Docker
    • Linux
    • Prometheus
    • operations
    • security
    • trouble-shooting
  • distributed-knowledge
    • Zookeeper_CMD
    • distributed-system
  • game-engine
    • Unity
  • others
    • appium使用
  • protocols
    • http(s)协议
    • 官方链接
    • sip
  • storage
    • Elasticsearch
    • GuavaCache
    • MySQL
    • MySQL_CMD
    • NoSQL
    • Redis
    • Redis_CMD
  • system-design
    • system-design
  • tools
    • Git
    • IDEA
    • Mac
    • VScode
    • Vim
  • _working
    • doc-template
      • backend-design-review
      • correction-of-error
      • service-review
    • process
      • domain-backup
      • oncall
  • blogs
    • history
      • 8088/8086微处理器
      • 8088/8086指令系统
      • CSS-DOM
      • CSS定位
      • CSS工作原理
      • CSS控制背景
      • CSS浮动布局
      • CSS盒模型
      • Chrome开发者工具使用方法
      • DOM
      • Django Model模型层学习
      • Django-REST-framework Serializers学习
      • Django-REST-framework Views和ViewSets学习
      • Django View视图层学习
      • Gvim下Emmet安装及使用教程
      • HTTP协议简介
      • HashMap原理初探
      • JavaScript简史
      • JavaScript语法
      • Java内存模型和GC机制
      • Java基础——Lambda学习
      • Java基础——方法引用
      • Java基础——枚举类型
      • Java类加载机制
      • KMP算法
      • Kafka学习
      • Linux下用命令行编译Java程序
      • MathJax简介和基本用法
      • Python实现常见数据结构
      • Python装饰器总结
      • TCP协议的三次握手和四次挥手
      • Thrift学习
      • asyncio学习
      • markdown的常用语法
      • 修改hosts文件实现翻墙
      • 充实文档的内容
      • 关系数据库
      • 关系数据库标准语言SQL(一)
      • 关系数据库标准语言SQL(二)
      • 关系数据理论
      • 关系查询处理和查询优化
      • 内联元素和块级元素
      • 剑指offer算法题练习
      • 动态创建标记
      • 图形化用户界面
      • 在Eclipse中使用Maven构建Java Web项目
      • 增加微博秀遇到的一些问题
      • 处理机调度
      • 如何用github和hexo搭建个人博客
      • 存储管理
      • 存储系统的层次结构
      • 学习模仿lionhit网站首页的过程总结
      • 实用的GitHub小技巧
      • 并发控制
      • 循环与分支程序设计
      • 指令系统的设计
      • 指令级并行及其开发——硬件方法
      • 搭建自己的VPN服务器
      • 操作系统用户界面
      • 数据库安全性
      • 数据库完整性
      • 数据库恢复技术
      • 数据库绪论
      • 数据库编程
      • 数据库设计
      • 数据抽象
      • 文件系统
      • 文法和语言
      • 最佳实践
      • 案例研究:JavaScript图片库
      • 案例研究:图片库改进版
      • 汇编语言程序格式
      • 汇编语言程序设计基础知识
      • 流水线技术
      • 深度优先搜索和广度优先搜索
      • 牛客网——网易2017秋招编程题集合
      • 用JavaScript实现动画效果
      • 第一篇博客
      • 经典排序算法总结(Java实现)
      • 经典查找算法总结(Java实现)
      • 综合示例
      • 编译原理引论
      • 背包、队列和栈
      • 虚拟机安装Linux系统及常用软件
      • 计算机操作系统绪论
      • 计算机系统结构的基础知识
      • 设备管理
      • 设计模式之代理模式
      • 设计模式之单例模式
      • 设计模式之工厂模式
      • 设计模式之策略模式
      • 设计模式之观察者模式
      • 词法分析
      • 进程管理
      • 闭包
      • 阻止Google自动跳转到香港服务器的方法
      • 项目部署过程
  • programming-language
    • C#
      • C#
    • C&C++
      • C
    • C&C++
      • C++
    • Java
      • GoogleGuice
    • Java
      • JVM
    • Java
      • Java
    • Java
      • Maven
    • Java
      • Mybatis
    • Java
      • Spring知识
    • Java
      • SpringBoot
    • Java
      • Tomcat
    • Python
      • Python
    • Shell
      • Shell
  • wheels
    • dcc
      • 产品调研
      • 方案设计
    • red-envelope
      • 方案设计
    • short-url
      • 短链接服务
    • sso
      • 方案设计
Powered by GitBook
On this page
  • 装饰器简介
  • 简单装饰器
  • 带参数的装饰器
  • 类式装饰器
  • 内置装饰器
  • 装饰器顺序
  1. blogs
  2. history

Python装饰器总结

本质上,Python装饰器就是一个返回函数的高阶函数。形式与Java注解很相似,但Python装饰器是通过函数式编程来实现,而Java注解是通过反射来实现。而且两者功能也不同,Python的装饰器会改变被装饰对象的行为和性质,而Java的注解只是元数据,即一种描述数据的数据,它本身不改变被注解对象的行为和性质,只有理解这些注解的处理器才会改变行为。

装饰器简介

装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器就是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概况地讲,装饰器的作用就是为已经存在的对象添加额外的功能。

简单装饰器

def log(func):
    def wrapper(*args, **kwargs):
        print('%s function start' % func.__name__)
        result = func(*args, **kwargs)
        print('%s function end' % func.__name__)
        return result
    return wrapper
 
@log
def say1(words):
    print(words)

@log
def say2(words):
	return words
 
say1('hello')	# 此时say1和say2指向的是wrapper函数(__name__为'wrapper')

data = say2('hello')
print(data)
# 均打印:
# say function start
# hello
# say function end

log函数对象就是一个装饰器,它接受一个函数作为参数,并返回一个函数。@符号是装饰器的语法糖,把@log放到say()函数的定义处,相当于执行了语句:

say = log(say)

由于log()是一个decorator,返回一个函数,所以,原来的say()函数仍然存在,只是现在同名的say变量指向了新的函数,于是调用say()将执行新函数,即在log()函数中返回的wrapper()函数。 wrapper()函数的参数定义是(*args, **kwargs),因此,wrapper()函数可以接受任意参数的调用。在wrapper()函数内,首先打印函数开始执行日志,再紧接着调用原始函数,然后打印函数执行结束日志,最后返回原始函数返回值。

带参数的装饰器

Python装饰器还可以有更大的灵活性,它可以允许我们提供相应的参数。

def log(level):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if level == 'warning':
                print('waring! %s is called' % func.__name__)
            elif level == 'error':
                print('error! %s is called' % func.__name__)
            else:
                print('%s is called' % func.__name__)
            return func(*args, **kwargs)
        return wrapper
    return decorator


@log('error')
def say(words):
    print(words)

say('hello')
# 打印:
# error! say is called
# hello

f = say
print(f.__name__)
# 打印:
# wrapper

它实际上是对原有装饰器的一个函数封装,并返回一个装饰器。我们可以将它理解为一个含有参数的闭包。当我们使用@log('error')调用的时候,Python能够发现这一层的封装,并把参数传递到装饰器环境中。 和两层嵌套的decorator相比,三层嵌套的效果相当于:

say = log('error')(say)

注意: 使用装饰器极大地复用了代码,但是有一个缺点就是原函数的元信息不见了,比如函数的_name_、docstring、参数列表,这样可能会造成某些依赖函数元信息的代码执行出错。好在我们有functools.wraps,wraps本身也是一个装饰器,它能把原函数的元信息拷贝到装饰器中,所以完整的decorator的写法如下:

import functools

def log(func):
	@functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('%s function was called.' % func.__name__)
        return func(*args, **kwargs)
    return wrapper

类式装饰器

相比函数装饰器,类式装饰器具有灵活度大、高内聚、封装性等优点。使用类式装饰器可以依靠内部的__call__方法,当使用@形式将装饰器附加到函数上时,就会调用此方法。

不带参数的类式装饰器

class log(object):

    def __init__(self, func):
        self._func = func

    def __call__(self, *args, **kwargs):
        print('start')
        return self._func(*args, **kwargs)

@log
def say(words):
    return words

res = say('hello')
print(res)

res = say('world')
print(res)

# 打印:
# start
# hello
# start
# world

在装饰阶段,_init_ 函数执行,在被装饰的方法被调用的时候,_call_ 执行。

带参数的类式装饰器

class log(object):

    def __init__(self, level):
        self._level = level

    def __call__(self, func):
        print('__call__()方法执行')
        def decorator(*args, **kwargs):
            if self._level == 'warning':
                print('warning! function was called')
            elif self._level == 'error':
                print('error! function was called')
            return func(*args, **kwargs)
        return decorator


@log('error')
def say(words):
    return words

res = say('hello')
print(res)

res = say('world')
print(res)

# 打印:
# __call__()方法执行
# error! function was called
# hello
# error! function was called
# world

__call__只会在装饰阶段被调用一次。

内置装饰器

  1. @staticmethod 类静态方法,其跟成员方法的区别是没有self参数,并且可以在类不进行实例化的情况下调用

  2. @classmethod 与成员方法的区别在于所接收的第一个参数不是self(类实例的指针),而是cls(当前类)

  3. @property 属性,表示可以通过类实例访问的信息

装饰器顺序

@a
@b
@c
def f():

等效于:

f = a(b(c(f)))

PreviousPython实现常见数据结构NextTCP协议的三次握手和四次挥手

Last updated 3 years ago

如何理解Python装饰器? - 知乎
装饰器 - 廖雪峰的官方网站