Skip to content
SRE运维进阶之路SRE运维进阶之路
github icon
    • 1 Python 简介

      • 1.1 初识Python
        • 1.2 Python 代码规范
          • 1.3 Python 虚拟环境
            • 1.4 使用 vscode 打造 python 开发环境
              • 1.5 pypi 配置国内镜像
              • 2 Python 基础

                • 2.1 Python基础语法
                  • 2.2 程序控制
                    • 2.3 Python数据类型

                      • 2.3.1 数值型
                        • 2.3.2 字符串 str
                          • 2.3.3 字节序列
                            • 2.3.4 列表 list & 元组 tuple
                              • 2.3.5 集合 set & 字典 dict
                            • 3 高级特性

                              • 3.1 线性结构特征 可迭代 & 切片
                                • 3.2 列表、集合、字典解析式
                                  • 3.3 生成器
                                    • 3.4 迭代器
                                    • 4 函数

                                      • 4.1 函数的定义 & 调用 & 返回值
                                        • 4.2 函数参数
                                          • 4.3 作用域
                                            • 4.4 递归函数
                                            • 5 函数式编程

                                              • 5.1 高阶函数
                                                • 5.2 返回函数
                                                  • 5.3 匿名函数
                                                    • 5.4 装饰器
                                                      • 由来
                                                        • 无参装饰器
                                                          • 日志记录装饰器实现
                                                            • 文档字符串
                                                              • 带参装饰器
                                                                • 进阶
                                                                  • 时间模块
                                                                    • datetime 模块
                                                                      • 日期与格式化
                                                                        • timedelta 类
                                                                          • time 模块
                                                                        • 5.5 偏函数
                                                                        • 6 模块

                                                                          • 6.1 Python 模块常用的几种安装方式
                                                                            • 6.2 Python 的 setup.py 详解
                                                                            • 7 IO编程

                                                                              • 7.1 操作文件和目录
                                                                                • 7.2 序列化和反序列化
                                                                                • 8 异常、调试和测试

                                                                                  • 8.1 异常处理
                                                                                  • 9 面向对象编程

                                                                                    • 9.1 类、实例和封装
                                                                                      • 9.2 访问控制和属性装饰器
                                                                                        • 9.3 继承、多态和Mixin
                                                                                        • 10 进程和线程

                                                                                          • 10.1 多进程
                                                                                            • 10.2 多线程
                                                                                              • 10.2 线程同步
                                                                                              • 11 网络编程

                                                                                                • 11.1 SocketServer
                                                                                                  • 11.2 TCP 编程
                                                                                                  • 11 魔术方法
                                                                                                    • 17 IO 模型
                                                                                                      • python 实际工作中的实例
                                                                                                      • 前端学习笔记

                                                                                                        5.4 装饰器

                                                                                                        author iconClaycalendar icon2021年5月11日category icon
                                                                                                        • Python
                                                                                                        timer icon大约 6 分钟

                                                                                                        此页内容
                                                                                                        • 由来
                                                                                                        • 无参装饰器
                                                                                                        • 日志记录装饰器实现
                                                                                                        • 文档字符串
                                                                                                        • 带参装饰器
                                                                                                        • 进阶
                                                                                                        • 时间模块
                                                                                                          • datetime 模块
                                                                                                          • 日期与格式化
                                                                                                          • timedelta 类
                                                                                                          • time 模块

                                                                                                        # 5.4 装饰器

                                                                                                        # 由来

                                                                                                        需求:为一个加法函数增加记录实参的功能

                                                                                                        def add(x, y):
                                                                                                            print('add called. x={}, y={}'.format(x, y)) # 增加的记录功能
                                                                                                            return x + y
                                                                                                        
                                                                                                        add(4, 5)
                                                                                                        
                                                                                                        1
                                                                                                        2
                                                                                                        3
                                                                                                        4
                                                                                                        5

                                                                                                        上面的代码满足了需求,但有缺点:

                                                                                                        记录信息的功能,可以是一个单独的功能。显然和add函数耦合太紧密。加法函数属于业务功能,输出信息属于非功能代码,不该放在add函数中

                                                                                                        1、提供一个函数logger完成记录功能

                                                                                                        def add(x, y):
                                                                                                            return x + y
                                                                                                        
                                                                                                        def logger(fn):
                                                                                                            print('调用前增强')
                                                                                                            ret = fn(4, 5)
                                                                                                            print('调用后增强')
                                                                                                            return ret
                                                                                                        
                                                                                                        print(logger(add))
                                                                                                        
                                                                                                        1
                                                                                                        2
                                                                                                        3
                                                                                                        4
                                                                                                        5
                                                                                                        6
                                                                                                        7
                                                                                                        8
                                                                                                        9
                                                                                                        10

                                                                                                        2、改进传参

                                                                                                        def add(x, y):
                                                                                                            return x + y
                                                                                                        
                                                                                                        def logger(fn, *args, **kwargs):
                                                                                                            print('调用前增强')
                                                                                                            ret = fn(*args, **kwargs)
                                                                                                            print('调用后增强')
                                                                                                            return ret
                                                                                                        
                                                                                                        print(logger(add, 4, 5))
                                                                                                        
                                                                                                        1
                                                                                                        2
                                                                                                        3
                                                                                                        4
                                                                                                        5
                                                                                                        6
                                                                                                        7
                                                                                                        8
                                                                                                        9
                                                                                                        10

                                                                                                        3、柯里化

                                                                                                        def add(x, y):
                                                                                                            return x + y
                                                                                                        
                                                                                                        def logger(fn):
                                                                                                            def wrapper( *args, **kwargs):
                                                                                                                print('调用前增强')
                                                                                                                ret = fn(*args, **kwargs)
                                                                                                                print('调用后增强')
                                                                                                                return ret
                                                                                                            return wrapper
                                                                                                        
                                                                                                        inner = logger(add)
                                                                                                        x = inner(4, 5)
                                                                                                        print(x)
                                                                                                        
                                                                                                        1
                                                                                                        2
                                                                                                        3
                                                                                                        4
                                                                                                        5
                                                                                                        6
                                                                                                        7
                                                                                                        8
                                                                                                        9
                                                                                                        10
                                                                                                        11
                                                                                                        12
                                                                                                        13
                                                                                                        14

                                                                                                        再进一步

                                                                                                        def add(x, y):
                                                                                                            return x + y
                                                                                                        
                                                                                                        def logger(fn):
                                                                                                            def wrapper( *args, **kwargs):
                                                                                                                print('调用前增强')
                                                                                                                ret = fn(*args, **kwargs)
                                                                                                                print('调用后增强')
                                                                                                                return ret
                                                                                                            return wrapper
                                                                                                        
                                                                                                        add = logger(add)
                                                                                                        print(add(4, 5))
                                                                                                        
                                                                                                        1
                                                                                                        2
                                                                                                        3
                                                                                                        4
                                                                                                        5
                                                                                                        6
                                                                                                        7
                                                                                                        8
                                                                                                        9
                                                                                                        10
                                                                                                        11
                                                                                                        12
                                                                                                        13

                                                                                                        4、装饰器语法

                                                                                                        def logger(fn):
                                                                                                            def wrapper( *args, **kwargs):
                                                                                                                print('调用前增强')
                                                                                                                ret = fn(*args, **kwargs)
                                                                                                                print('调用后增强')
                                                                                                                return ret
                                                                                                            return wrapper
                                                                                                        
                                                                                                        @logger # 等价于 add = wrapper <=> add = logger(add)
                                                                                                        def add(x, y):
                                                                                                            return x + y
                                                                                                        
                                                                                                        print(add(4, 5))
                                                                                                        
                                                                                                        1
                                                                                                        2
                                                                                                        3
                                                                                                        4
                                                                                                        5
                                                                                                        6
                                                                                                        7
                                                                                                        8
                                                                                                        9
                                                                                                        10
                                                                                                        11
                                                                                                        12
                                                                                                        13

                                                                                                        @logger就是装饰器语法

                                                                                                        等价式非常重要,如果你不能理解装饰器,开始的时候一定要把等价式写在后面

                                                                                                        # 无参装饰器

                                                                                                        • 上例的装饰器语法,称为无参装饰器
                                                                                                        • @符号后是一个函数
                                                                                                        • 虽然是无参装饰器,但是@后的函数本质上是单参函数
                                                                                                        • 上例的 logger 函数是一个高阶函数

                                                                                                        # 日志记录装饰器实现

                                                                                                        import time
                                                                                                        import datetime
                                                                                                        
                                                                                                        def logger(fn):
                                                                                                            def wrapper(*args, **kwargs):
                                                                                                                print('调用前增强')
                                                                                                                start = datetime.datetime.now()
                                                                                                                ret = fn(*args, **kwargs) # 参数解构
                                                                                                                print('调用后增强')
                                                                                                                delta = (datetime.datetime.now() - start).total_seconds()
                                                                                                                print('Function {} took {}s.'.format(fn.__name__, delta))
                                                                                                                return ret
                                                                                                            return wrapper
                                                                                                        
                                                                                                        @logger # 等价于 add = wrapper <=> add = logger(add)
                                                                                                        def add(x, y):
                                                                                                            time.sleep(2)
                                                                                                            return x + y
                                                                                                        
                                                                                                        print(add(100, 200))
                                                                                                        
                                                                                                        1
                                                                                                        2
                                                                                                        3
                                                                                                        4
                                                                                                        5
                                                                                                        6
                                                                                                        7
                                                                                                        8
                                                                                                        9
                                                                                                        10
                                                                                                        11
                                                                                                        12
                                                                                                        13
                                                                                                        14
                                                                                                        15
                                                                                                        16
                                                                                                        17
                                                                                                        18
                                                                                                        19
                                                                                                        20

                                                                                                        # 文档字符串

                                                                                                        • Python 文档字符串 Documentation Strings
                                                                                                        • 在函数(类、模块)语句块的第一行,且习惯是多行的文本,所以多使用三引号
                                                                                                        • 文档字符串也算是合法的一条语句
                                                                                                        • 惯例是首字母大写,第一行写概述,空一行,第三行写详细描述
                                                                                                        • 可以使用特殊属性__doc__访问这个文档
                                                                                                        def add(x, y):
                                                                                                            """这是加法函数的文档"""
                                                                                                            return x + y
                                                                                                        
                                                                                                        print("{}'s doc = {}".format(add.__name__ , add.__doc__))
                                                                                                        
                                                                                                        1
                                                                                                        2
                                                                                                        3
                                                                                                        4
                                                                                                        5
                                                                                                        import time
                                                                                                        import datetime
                                                                                                        
                                                                                                        def logger(fn):
                                                                                                            def wrapper(*args, **kwargs):
                                                                                                                '''wrapper's doc'''
                                                                                                                print('调用前增强')
                                                                                                                start = datetime.datetime.now()
                                                                                                                ret = fn(*args, **kwargs) # 参数解构
                                                                                                                print('调用后增强')
                                                                                                                delta = (datetime.datetime.now() - start).total_seconds()
                                                                                                                print('Function {} took {}s.'.format(fn.__name__, delta))
                                                                                                                return ret
                                                                                                            return wrapper
                                                                                                        
                                                                                                        @logger # 等价于 add = wrapper <=> add = logger(add)
                                                                                                        def add(x, y):
                                                                                                            '''add's doc'''
                                                                                                            time.sleep(0.1)
                                                                                                            return x + y
                                                                                                        
                                                                                                        print(add(100, 200))
                                                                                                        
                                                                                                        print("name={}, doc={}".format(add.__name__, add.__doc__))
                                                                                                        
                                                                                                        1
                                                                                                        2
                                                                                                        3
                                                                                                        4
                                                                                                        5
                                                                                                        6
                                                                                                        7
                                                                                                        8
                                                                                                        9
                                                                                                        10
                                                                                                        11
                                                                                                        12
                                                                                                        13
                                                                                                        14
                                                                                                        15
                                                                                                        16
                                                                                                        17
                                                                                                        18
                                                                                                        19
                                                                                                        20
                                                                                                        21
                                                                                                        22
                                                                                                        23
                                                                                                        24

                                                                                                        被装饰后,函数名和文档都不对了。如何解决?

                                                                                                        functools模块提供了一个wraps装饰器函数,本质上调用的是update_wrapper,它就是一个属性复制函数。

                                                                                                        wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)

                                                                                                        • wrapped 就是被包装函数
                                                                                                        • wrapper 就是包装函数
                                                                                                        • 用被包装函数的属性覆盖包装函数的同名属性
                                                                                                        • 元组 WRAPPER_ASSIGNMENTS 中是要被覆盖的属性
                                                                                                          • __module__ , __name__ , __qualname__ , __doc__ , __annotations__
                                                                                                          • 模块名、名称、限定名、文档、参数注解
                                                                                                        import time
                                                                                                        import datetime
                                                                                                        from functools import wraps
                                                                                                        
                                                                                                        def logger(fn):
                                                                                                            @wraps(fn) # 用被包装函数fn的属性覆盖包装函数wrapper的同名属性
                                                                                                            def wrapper(*args, **kwargs):
                                                                                                                '''wrapper's doc'''
                                                                                                                print('调用前增强')
                                                                                                                start = datetime.datetime.now()
                                                                                                                ret = fn(*args, **kwargs) # 参数解构
                                                                                                                print('调用后增强')
                                                                                                                delta = (datetime.datetime.now() - start).total_seconds()
                                                                                                                print('Function {} took {}s.'.format(fn.__name__, delta))
                                                                                                                return ret
                                                                                                            return wrapper
                                                                                                        
                                                                                                        @logger # 等价于 add = wrapper <=> add = logger(add)
                                                                                                        def add(x, y):
                                                                                                            '''add's doc'''
                                                                                                            time.sleep(0.1)
                                                                                                            return x + y
                                                                                                        
                                                                                                        print(add(100, 200))
                                                                                                        
                                                                                                        print("name={}, doc={}".format(add.__name__, add.__doc__))
                                                                                                        
                                                                                                        1
                                                                                                        2
                                                                                                        3
                                                                                                        4
                                                                                                        5
                                                                                                        6
                                                                                                        7
                                                                                                        8
                                                                                                        9
                                                                                                        10
                                                                                                        11
                                                                                                        12
                                                                                                        13
                                                                                                        14
                                                                                                        15
                                                                                                        16
                                                                                                        17
                                                                                                        18
                                                                                                        19
                                                                                                        20
                                                                                                        21
                                                                                                        22
                                                                                                        23
                                                                                                        24
                                                                                                        25
                                                                                                        26

                                                                                                        # 带参装饰器

                                                                                                        • @之后不是一个单独的标识符,是一个函数调用
                                                                                                        • 函数调用的返回值又是一个函数,此函数是一个无参装饰器
                                                                                                        • 带参装饰器,可以有任意个参数
                                                                                                          • @func()
                                                                                                          • @func(1)
                                                                                                          • @func(1, 2)

                                                                                                        # 进阶

                                                                                                        import datetime
                                                                                                        from functools import wraps
                                                                                                        
                                                                                                        def logger(fn):
                                                                                                            print('logger run')
                                                                                                            @wraps(fn) # 用被包装函数fn的属性覆盖包装函数wrapper的同名属性
                                                                                                            def wrapper(*args, **kwargs): # wrapper = wraps(fn)(wrapper)
                                                                                                                "wrapper's doc"
                                                                                                                print('wrapper run')
                                                                                                                start = datetime.datetime.now()
                                                                                                                ret = fn(*args, **kwargs) # 参数解构
                                                                                                                delta = (datetime.datetime.now() - start).total_seconds()
                                                                                                                print('Function {} took {}s.'.format(fn.__name__, delta))
                                                                                                                return ret
                                                                                                            return wrapper
                                                                                                        
                                                                                                        @logger # 等价于 add = wrapper <=> add = logger(add)
                                                                                                        def add(x, y):
                                                                                                            """add function"""
                                                                                                        
                                                                                                        @logger
                                                                                                        def sub(x, y):
                                                                                                            """sub function"""
                                                                                                        
                                                                                                        print(add.__name__, sub.__name__)
                                                                                                        
                                                                                                        1
                                                                                                        2
                                                                                                        3
                                                                                                        4
                                                                                                        5
                                                                                                        6
                                                                                                        7
                                                                                                        8
                                                                                                        9
                                                                                                        10
                                                                                                        11
                                                                                                        12
                                                                                                        13
                                                                                                        14
                                                                                                        15
                                                                                                        16
                                                                                                        17
                                                                                                        18
                                                                                                        19
                                                                                                        20
                                                                                                        21
                                                                                                        22
                                                                                                        23
                                                                                                        24
                                                                                                        25
                                                                                                        • logger 什么时候执行? 从上到下执行
                                                                                                        • logger 执行过几次? 两次
                                                                                                        • wraps 装饰器执行过几次? 两次 , 也是进入函数,从上到下依次执行,函数没调用前,跳过函数部分代码
                                                                                                        • wrapper 的 __name__ 等属性被覆盖过几次? 两次
                                                                                                        • add.__name__ 打印什么名称?add
                                                                                                        • sub.__name__ 打印什么名称?sub

                                                                                                        # 时间模块

                                                                                                        # datetime 模块

                                                                                                        datetime类

                                                                                                        • 时间高级类
                                                                                                        • 类方法,即使用类调用的方法,由类方法获得一个时间对象
                                                                                                          • now(tz=None) 返回当前时间的datetime对象,时间到微秒,如果tz为None,返回当前时区的不带时区信息的时间
                                                                                                          • utcnow() 不带时区的0时区时间
                                                                                                          • fromtimestamp(timestamp, tz=None) 从一个时间戳返回一个datetime对象
                                                                                                        • 时间对象方法
                                                                                                          • timestamp() 返回一个到微秒的时间戳
                                                                                                            • 时间戳:格林威治时间1970年1月1日0点到现在的秒数
                                                                                                          • 构造方法 datetime.datetime(2016, 12, 6, 16, 29, 43, 79043)
                                                                                                          • year、month、day、hour、minute、second、microsecond,取datetime对象的年月日时分秒及微秒
                                                                                                          • weekday() 返回星期的天,周一0,周日6
                                                                                                          • isoweekday() 返回星期的天,周一1,周日7
                                                                                                          • date() 返回日期date对象
                                                                                                          • time() 返回时间time对象
                                                                                                        import datetime
                                                                                                        
                                                                                                        # 类方法获得时间对象
                                                                                                        print(datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=8)))) # 时区时间
                                                                                                        print(datetime.datetime.now()) # 无时区时间
                                                                                                        print(datetime.datetime.utcnow()) # UTC时间,可以认为是GMT或0时区时间
                                                                                                        
                                                                                                        # 时间戳操作
                                                                                                        stamp = datetime.datetime.now().timestamp() # 获得时间戳
                                                                                                        print(stamp)
                                                                                                        dt = datetime.datetime.fromtimestamp(stamp) # 从时间戳获得时间对象
                                                                                                        print(dt)
                                                                                                        
                                                                                                        print(type(dt.date()), dt.date())
                                                                                                        print(type(dt.time()), dt.time())
                                                                                                        
                                                                                                        1
                                                                                                        2
                                                                                                        3
                                                                                                        4
                                                                                                        5
                                                                                                        6
                                                                                                        7
                                                                                                        8
                                                                                                        9
                                                                                                        10
                                                                                                        11
                                                                                                        12
                                                                                                        13
                                                                                                        14
                                                                                                        15

                                                                                                        # 日期与格式化

                                                                                                        • 类方法 strptime(date_string, format) ,返回datetime对象(时间字符串+格式化字符串 => 时间对象)
                                                                                                        • 对象方法 strftime(format) ,返回字符串(时间对象通过格式字符串 => 时间字符串)
                                                                                                        • 字符串format函数格式化(时间对象通过格式字符串 => 时间字符串)
                                                                                                        import datetime
                                                                                                        
                                                                                                        datestr = '2018-01-10 17:16:08'
                                                                                                        dt = datetime.datetime.strptime(datestr, '%Y-%m-%d %H:%M:%S') #由字符串到时间对象
                                                                                                        print(type(dt), dt)
                                                                                                        print(dt.strftime('%Y/%m/%d-%H:%M:%S')) # 输出为字符串
                                                                                                        print("{:%Y/%m/%d %H:%M:%S}".format(dt)) # 输出为字符串
                                                                                                        
                                                                                                        1
                                                                                                        2
                                                                                                        3
                                                                                                        4
                                                                                                        5
                                                                                                        6
                                                                                                        7

                                                                                                        # timedelta 类

                                                                                                        • datetime2 = datetime1 + timedelta
                                                                                                        • datetime2 = datetime1 - timedelta
                                                                                                        • timedelta = datetime1 - datetime2
                                                                                                        • 构造方法
                                                                                                          • datetime.timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0,hours=0, weeks=0)
                                                                                                          • year = datetime.timedelta(days=365)
                                                                                                        • timedelta对象有方法total_seconds(), 返回时间差的总秒数

                                                                                                        # time 模块

                                                                                                        • time.sleep(secs) 将调用线程挂起指定的秒数
                                                                                                        edit icon编辑此页open in new window
                                                                                                        上次编辑于: 2021/6/23 03:47:00
                                                                                                        贡献者: clay-wangzhi
                                                                                                        上一页
                                                                                                        5.3 匿名函数
                                                                                                        下一页
                                                                                                        5.5 偏函数
                                                                                                        备案号:冀ICP备2021007336号
                                                                                                        Copyright © 2023 Clay