首页 > 编程语言 >python进阶——装饰器

python进阶——装饰器

时间:2022-09-22 16:16:07浏览次数:46  
标签:timeit 进阶 python 装饰 func time print hello def

万物皆对象

介绍装饰器之前,我们需要理解一个概念:在介绍装饰器前,我们需要理解一个概念:在 Python 开发中,一切皆对象

什么意思呢?

就是我们在开发中,无论是定义的变量(数字、字符串、元组、列表、字典)、还是方法、类、实例、模块,这些都可以称作对象

怎么理解呢?在 Python 中,所有的对象都会有属性和方法,也就是说可以通过「.」去获取它的属性或调用它的方法,例如像下面这样:

i = 10  # int对象
print(id(i))  # 获取i的内存地址
print(type(i))  # 获取i的类型
s = 'hello'  # str 对象
print(id(s))  # 获取s的内存地址
print(type(s))  # 获取s的类型
​
def hello(): # function对象
    print ('Hello World')
print (id(hello)),
print(type(hello)),
print(hello.__name__)
hello2 = hello
print (id(hello2)),
print(type(hello2)),
print(hello2.__name__)
​
out:
140704515711920
<class 'int'>
2637870361712
<class 'str'>
2637873499536
<class 'function'>
hello
2637873499536
<class 'function'>
hello

 

我们可以看到,常见的这些类型:intstrdictfunction,甚至 classinstance 都可以调用 idtype 获得对象的唯一标识和类型。

例如方法的类型是 function,类的类型是 type,并且这些对象都是可传递的。

方法对象可传递会带来什么好处呢?

这么做的好处就是,我们可以实现一个「闭包」,而「闭包」就是实现一个装饰器的基础。

闭包

闭包的概念:概念:函数里面定义函数

闭包的三个条件:

1.函数中嵌套一个函数

2.外层函数返回内层函数的变量名

3.内层函数对外部作用域有一个非全局变量进行引用

闭包的作用:提高稳定性,实现数据的锁定

闭包举例:

假设我们现在想统计一个方法的执行时间,通常实现的逻辑如下:

*# coding: utf8*

import time

def hello(): 
    start = time.time() *# 开始时间* 
    time.sleep(1) *# 模拟执行耗时*
    print 'hello' end = time.time() *# 结束时间*
    print 'duration time: %ds' % int(end - start) *# 计算耗时*

hello()

*# Output:* *# hello* *# duration time: 1s*

 

统计一个方法执行时间的逻辑很简单,只需要在调用这个方法的前后,增加时间的记录就可以了。

但是,统计这一个方法的执行时间这么写一次还好,如果我们想统计任意一个方法的执行时间,每个方法都这么写,就会有大量的重复代码,而且不宜维护。

如何解决?这时我们通常会想到,可以把这个逻辑抽离出来:

`import time`

`def timeit(func):  *# 计算方法耗时的通用方法*`
  `start = time.time()`
  `func()     *# 执行方法*`
  `end = time.time()`
  `print 'duration time: %ds' % int(end - start)`

`def hello():`
  `time.sleep(1)`
  `print 'hello'`

`timeit(hello)  *# 调用执行*`

 

 

这里我们定义了一个 timeit 方法,而参数传入一个方法对象,在执行完真正的方法逻辑后,计算其运行时间。

这样,如果我们想计算哪个方法的执行时间,都按照此方式调用即可。

timeit(func1) *# 计算func1执行时间* timeit(func2) *# 计算func2执行时间*

虽然此方式可以满足我们的需求,但有没有觉得,本来我们想要执行的是 hello 方法,现在执行都需要使用 timeit 然后传入 hello 才能达到要求,有没有一种方式,既可以给原来的方法加上计算时间的逻辑,还能像调用原方法一样使用呢?

答案当然是可以的,我们对 timeit 进行改造:

*`# coding: utf8*`

`import time`

`def timeit(func):`
  `def inner():`
    `start = time.time()`
    `func()`
    `end = time.time()`
    `print 'duration time: %ds' % int(end - start)`
  `return inner`

`def hello():`
  `time.sleep(1)`
  `print 'hello'`

`hello = timeit(hello)  *# 重新定义hello*`
`hello()    *# 像调用原始方法一样使用*`

 

 

注意观察timeit的变动,它在内部定义了一个inner方法,此方法内部的实现与之前类似,但是,timeit最终返回的不是一个值,而是inner对象。所以当我们调用hello = timeit(hello)时,会得到一个方法对象,那么此时变量hello其实是inner,在执行hello()时,真正执行的是inner方法。我们对hello方法进行了重新定义,这么一来,hello不仅保留了其原有的逻辑,而且还增加了计算方法执行耗时的新功能。回过头来,我们分析一下timeit这个方法是如何运行的?在 Python 中允许在一个方法中嵌套另一个方法,这种特殊的机制就叫做「闭包」,这个内部方法可以保留外部方法的作用域,尽管外部方法不是全局的,内部方法也可以访问到外部方法的参数和变量。

装饰器

明白了闭包的工作机制后,那么实现一个装饰器就变得非常简单了。Python 支持一种装饰器语法糖「@」,使用这个语法糖,我们也可以实现与上面完全相同的功能:

# coding: utf8

@timeit   # 相当于 hello = timeit(hello)
def hello():
    time.sleep(1)
    print 'hello'

hello()  # 直接调用原方法即可

 

 

看到这里,是不是觉得很简单?这里的@timeit其实就等价于hello = timeit(hello)。装饰器本质上就是实现一个闭包,把一个方法对象当做参数,传入到另一个方法中,然后这个方法返回了一个增强功能的方法对象。这就是装饰器的核心,平时我们开发中常见的装饰器,无非就是这种形式的变形而已。

装饰器原理:

1、装饰器可以实现在不更改原有代码的前提下扩展新的功能

2、开放封闭原则:已实现的功能可扩展,但不能修改

 

实现一个简单装饰器:

# 实现一个简单装饰器
def func_a(fun):
    print("func_a调用了")
    def func_b():
        print("先洗手")
        fun()
    return func_b

@func_a
def func_demo():
    print("欢迎进入首页")

func_demo()


#func_a调用了
#先洗手
#欢迎进入首页

 

 

带参数的装饰器:

def add(func):
    def fun(a,b):
        print('相乘',a*b)
        print('相除',a/b)
        func(a,b)
    return fun

@add
def add_num(a,b):
    print("相加",a+b)

(add_num(2,4))
# 相乘 8
# 相除 0.5
# 相加 6

 

 

通用装饰器

def add(func):
    def fun(*args,**kwargs):
        print("装饰器的功能代码:登录")
        func(*args,**kwargs)
    return fun
@add
def index():
    print("这是网站首页")
@add
def goods_list(num):
    print("这是商品列表第{}页".format(num))
a = index
a()
b = goods_list
b(9)
​
# 装饰器的功能代码:登录
# 这是网站首页
# 装饰器的功能代码:登录
# 这是商品列表第9页

 

 

类装饰器

def add(func):
    def fun(*args, **kwargs):
        print("装饰器的功能代码:登录")
        return func(*args, **kwargs)  # 类级别一定要有return,否则会返回None
    return fun
@add
class Myclass:
    def __init__(self, name, pw):
        self.name = name
        self.pw = pw
m = Myclass('xiucai', 123)
print(m.pw)
print(m.name)

# 装饰器的功能代码:登录
# 123
# xiucai

 

 

以上文章部分参考:https://zhuanlan.zhihu.com/p/425626332

 

标签:timeit,进阶,python,装饰,func,time,print,hello,def
From: https://www.cnblogs.com/handouergege/p/16719640.html

相关文章

  • Python3交叉编译步骤(二)-三方库的交叉编译
    一.项目场景在cortex-A9主板上运行python3,能够使用常用的三方库二.配置主机环境:ubuntu-18.04-x86_64(虚拟机)交叉编译链:arm-linux-gnueabihf-gcc开发板:cortex-A9(armv7l)三.......
  • python解释器下载与安装+配置环境变量
    一.python解释器的下载与安装......
  • Python 简介
    ###本简介浓缩了一些基本概念,并且随着学习会不断增加跟新### Python是一种解释型的面向对象的语言。由GuidoVanRossum于1989年发明,1991年公布。网站www.python.o......
  • 装饰器详解
    一、闭包了解装饰器前,首先要了解,什么是闭包。闭包就是在一个函数中再定义一个函数,内部函数需要引用外部函数的参数,且外部函数的返回值是内部函数。defoutside(x):......
  • Pycharm的安装并且连接已有的Python环境实现自由编译(附中文配置)|并通过Pycharm实现增加
    Python环境的配置 通过python的官方网站:python.org即可进入python的官网-->选择Downloads即可进入选择版本的界面,在界面中选择自己想要下载的版本即可,下载好之后在安装界......
  • python解释器
    计算机五大组成部分1、控制器控制计算机各个硬件工作2、运算器数学运算、逻辑运算控制器+运算器=cpu(中央处理器)3、存储器 存储数据 内存:存在缓存中,断电数据丢失 ......
  • python系列——常用系统模块
    常用系统模块os:与操作系统交互os.name/os.sep/os.linesep.:系统名称/系统路径分隔符/系统换行符os.mkdir()/os.makedirs():建立目录/建立多级目录os.getenv("PAT......
  • python 列表
    列表(list),是一个有序且可变的容器,在里面可以存放多个不同类型的元素。1.定义不可变类型:字符串、布尔、整型(已最小,内部数据无法进行修改)可变类型:列表(内部数据元素......
  • Python 2
    实例一:print("今有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二,问几何?\n")#输入一个数number=int(input("请输入您认为符合条件的数:"))#判断是否符合条件ifnumber......
  • python -字符串和列表的翻转
    #1.给你一个字符串请实现字符串的翻转?name="人生若只如初见,何时秋风悲画扇" #2.给你一个列表如何实现列表翻转?name=[1,2,3,4,'a','b','c'] 答案在文......