装饰器的详细使用

(1)小知识点补充

在这里我们先学一个简单的知识点。

li = ['alex', '银角', '女神', 'egon', '太白']
for i in enumerate(li):
print(i)
for index, name in enumerate(li, 1):
print((index, name))
for index, name in enumerate(li, 100): # 起始位置默认是0,可更改
print((index, name))
'''
输出的结果为:
(0, 'alex')
(1, '银角')
(2, '女神')
(3, 'egon')
(4, '太白')
(1, 'alex')
(2, '银角')
(3, '女神')
(4, 'egon')
(5, '太白')
(100, 'alex')
(101, '银角')
(102, '女神')
(103, 'egon')
(104, '太白')
'''

(2)闭包

下面讲解闭包这个概念是参考博客太白金星。希望大家有不懂的地方可以问我,可以共同讨论学习。

由于闭包这个概念比较难以理解,尤其是初学者来说,相对难以掌握,所以我们通过示例去理解学习闭包。

给大家提个需求,然后用函数去实现:完成一个计算不断增加的系列值的平均值的需求。

例如:整个历史中的某个商品的平均收盘价。什么叫平局收盘价呢?就是从这个商品一出现开始,每天记录当天价格,然后计算他的平均值:平均值要考虑直至目前为止所有的价格。

比如大众推出了一款新车:小白轿车。

第一天价格为:100000元,平均收盘价:100000元

第二天价格为:110000元,平均收盘价:(100000 + 110000)/2 元

第三天价格为:120000元,平均收盘价:(100000 + 110000 + 120000)/3 元

........

series = []
def make_averager(new_value):
series.append(new_value)
total = sum(series)
return total / len(series) print(make_averager(100000))
print(make_averager(110000))
print(make_averager(120000))

从上面的例子可以看出,基本上完成了我们的要求,但是这个代码相对来说是不安全的,因为你的这个series列表是一个全局变量,只要是全局作用域的任何地方,都可能对这个列表进行改变。

series = []
def make_averager(new_value):
series.append(new_value)
total = sum(series)
return total / len(series) print(make_averager(100000))
print(make_averager(110000))
series.append(666) # 如果对数据进行相应改变,那么你的平均收盘价就会出现很大的问题,数据没有安全性。
print(make_averager(120000))

那么怎么办呢?有人说,你把他放在函数中不就行了,这样不就是局部变量了么?数据不就相对安全了么?

def make_averager(new_value):
series = []
series.append(new_value)
total = sum(series)
return total / len(series) print(make_averager(100000)) # 100000.0
print(make_averager(110000)) # 110000.0
print(make_averager(120000)) # 120000.0

这样计算的结果是不正确的,那是因为执行函数,会开启一个临时的名称空间,随着函数的结束而消失,所以你每次执行函数的时候,都是重新创建这个列表,那么这怎么做呢?这种情况下,就需要用到我们讲的闭包了,我们用闭包的思想改一下这个代码。

def make_averager():

    series = []
def averager(new_value):
series.append(new_value)
total = sum(series)
return total/len(series)
return averager avg = make_averager()
print(avg(100000))
print(avg(110000))
print(avg(120000))

大家仔细看一下这个代码,我是在函数中嵌套了一个函数。那么avg 这个变量接收的实际是averager函数名,也就是其对应的内存地址,我执行了三次avg 也就是执行了三次averager这个函数。那么此时你们有什么问题?

肯定有学生就会问,那么我的make_averager这个函数只是执行了一次,为什么series这个列表没有消失?反而还可以被调用三次呢?这个就是最关键的地方,也是闭包的精华所在。我给大家说一下这个原理,以图为证:

上面被红色方框框起来的区域就是闭包,被蓝色圈起来的那个变量应该是make_averager()函数的局部变量,它应该是随着make_averager()函数的执行结束之后而消失。但是他没有,是因为此区域形成了闭包,series变量就变成了一个叫自由变量的东西,averager函数的作用域会延伸到包含自由变量series的绑定。也就是说,每次我调用avg对应的averager函数 时,都可以引用到这个自用变量series,这个就是闭包。

闭包的定义:

  1. 闭包是嵌套在函数中的函数。

  2. 闭包必须是内层函数对外层函数的变量(非全局变量)的引用。

如何判断判断闭包?举例让同学回答:

# 例一:
def wrapper():
a = 1
def inner():
print(a)
return inner
ret = wrapper() # 例二:
a = 2
def wrapper():
def inner():
print(a)
return inner
ret = wrapper() # 例三: def wrapper(a,b):
def inner():
print(a)
print(b)
return inner
a = 2
b = 3
ret = wrapper(a,b)

以上三个例子,最难判断的是第三个,其实第三个也是闭包,如果我们每次去研究代码判断其是不是闭包,有一些不科学,或者过于麻烦了,那么有一些函数的属性是可以获取到此函数是否拥有自由变量的,如果此函数拥有自由变量,那么就可以侧面证明其是否是闭包函数了(了解):

def make_averager():

    series = []
def averager(new_value):
series.append(new_value)
total = sum(series)
return total/len(series) return averager
avg = make_averager()
# 函数名.__code__.co_freevars 查看函数的自由变量
print(avg.__code__.co_freevars) # ('series',)
当然还有一些参数,仅供了解: # 函数名.__code__.co_freevars 查看函数的自由变量
print(avg.__code__.co_freevars) # ('series',)
# 函数名.__code__.co_varnames 查看函数的局部变量
print(avg.__code__.co_varnames) # ('new_value', 'total')
# 函数名.__closure__ 获取具体的自由变量对象,也就是cell对象。
# (<cell at 0x0000020070CB7618: int object at 0x000000005CA08090>,)
# cell_contents 自由变量具体的值
print(avg.__closure__[0].cell_contents) # []

闭包的作用:保存局部信息不被销毁,保证数据的安全性(这是一个十分重要的作用)。

闭包的应用

  1. 可以保存一些非全局变量但是不易被销毁、改变的数据。
  2. 装饰器。

(3)装饰器

在使用装饰器之前,我们先开始了解一下开发封闭原则。那么什么是开发封闭原则呢?

# 开放封闭原则
# 开放:对代码的拓展是开放的。更新地图,加新枪,等等
# 封闭:对源码的修改是封闭的。闪躲用q。就是一个功能,一个函数。
# 别人用赤手空拳打你,用机枪扫你,扔雷.....这个功能不会改变。

1.对扩展是开放的

​ 我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。

2.对修改是封闭的

​ 就像我们刚刚提到的,因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个时候我们对函数内部进行修改,或者修改了函数的调用方式,很有可能影响其他已经在使用该函数的用户。

所以装饰器最终最完美的定义就是:在不改变原被装饰的函数的源代码以及调用方式下,为其添加额外的功能。

那么装饰器是什么呢?

# 什么叫做装饰器
# 装饰器:装饰,装修,房子本来可以住,如果装修,不影响你住
# 而且体验更加,让你的生活中增加了许多的功能,例如看电视,洗澡
# 器:工具
# 装饰器:完全遵循开放封闭原则。
# 装饰器:在不改变原函数的代码以及调用方式的前提下,为其增加新的功能

(4)标准版装饰器

代码优化:语法糖

根据我的学习,我们知道了,如果想要各给一个函数加一个装饰器应该是这样:

def home(name,age):
time.sleep(3) # 模拟一下网络延迟以及代码的效率
print(name,age)
print(f'欢迎访问{name}主页') def timer(func): # func = home
def inner(*args,**kwargs):
start_time = time.time()
func(*args,**kwargs)
end_time = time.time()
print(f'此函数的执行效率为{end_time-start_time}')
return inner home = timer(home)
home('太白',18)

如果你想给home加上装饰器,每次执行home之前你要写上一句:home = timer(home)这样你在执行home函数 home('太白',18) 才是真生的添加了额外的功能。但是每次写这一句也是很麻烦。所以,Python给我们提供了一个简化机制,用一个很简单的符号去代替这一句话。

def timer(func):  # func = home
def inner(*args,**kwargs):
start_time = time.time()
func(*args,**kwargs)
end_time = time.time()
print(f'此函数的执行效率为{end_time-start_time}')
return inner @timer # home = timer(home)
def home(name,age):
time.sleep(3) # 模拟一下网络延迟以及代码的效率
print(name,age)
print(f'欢迎访问{name}主页') home('太白',18)

你看此时我调整了一下位置,你要是不把装饰器放在上面,timer是找不到的。home函数如果想要加上装饰器那么你就在home函数上面加上@home,就等同于那句话 home = timer(home)。这么做没有什么特殊意义,就是让其更简单化,比如你在影视片中见过野战军的作战时由于不方便说话,用一些简单的手势代表一些话语,就是这个意思。

至此标准版的装饰器就是这个样子:

def wrapper(func):
def inner(*args,**kwargs):
'''执行被装饰函数之前的操作'''
ret = func(*args,**kwargs)
'''执行被装饰函数之后的操作'''
return ret
return inner

这个就是标准的装饰器,完全符合代码开放封闭原则。这几行代码一定要背过,会用。

python基础--闭包、装饰器的更多相关文章

  1. python基础—函数装饰器

    python基础-函数装饰器 1.什么是装饰器 装饰器本质上是一个python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能. 装饰器的返回值是也是一个函数对象. 装饰器经常用于有切 ...

  2. 十. Python基础(10)--装饰器

    十. Python基础(10)--装饰器 1 ● 装饰器 A decorator is a function that take a function as an argument and retur ...

  3. Day11 Python基础之装饰器(高级函数)(九)

    在python中,装饰器.生成器和迭代器是特别重要的高级函数   https://www.cnblogs.com/yuanchenqi/articles/5830025.html 装饰器 1.如果说装 ...

  4. python函数闭包-装饰器-03

    可调用对象 callable()  # 可调用的(这个东西加括号可以执行特定的功能,类和函数) 可调用对象即  callable(对象)  返回为  True  的对象 x = 1 print(cal ...

  5. [python基础]关于装饰器

    在面试的时候,被问到装饰器,在用的最多的时候就@classmethod ,@staticmethod,开口胡乱回答想这和C#的static public 关键字是不是一样的,等面试回来一看,哇,原来是 ...

  6. 1.16 Python基础知识 - 装饰器初识

    Python中的装饰器就是函数,作用就是包装其他函数,为他们起到修饰作用.在不修改源代码的情况下,为这些函数额外添加一些功能,像日志记录,性能测试等.一个函数可以使用多个装饰器,产生的结果与装饰器的位 ...

  7. 【Python基础】装饰器的解释和用法

    装饰器的用法比较简单,但是理解装饰器的原理还是比较复杂的,考虑到接下来的爬虫框架中很多用到装饰器的地方,我们先来讲解一下. 函数 我们定义了一个函数,没有什么具体操作,只是返回一个固定值 请注意一下缩 ...

  8. 【Python】 闭包&装饰器

    python中的函数本身就是对象,所以可以作为参数拿来传递.同时其允许函数的层级嵌套定义,使得灵活性大大增加. 闭包 闭包的定义:将函数的语句块与其运行所需要的环境打包到一起,得到的就是闭包对象.比如 ...

  9. python基础之 装饰器,内置函数

    1.闭包回顾 在学习装饰器之前,可以先复习一下什么是闭包? 在嵌套函数内部的函数可以使用外部变量(非全局变量)叫做闭包! def wrapper(): money =10 def inner(num) ...

  10. python基础-----函数/装饰器

    函数 在Python中,定义一个函数要使用def语句,依次写出函数名.括号.括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用return语句返回. 函数的优点之一是,可以将代码块与主程 ...

随机推荐

  1. Android Studio 插件 ADBWifi 无线调试真机

    长话短说,步骤如下 Android Studio 安装插件 ADB Wifi.这一步可以选择AS->Settings->Plugins->Market搜索:或者可以选择去插件官网下载 ...

  2. 如何在linux下安装tomcat服务器

    linux作为现在比较主流的服务器操作系统,使用的机器广泛,安全稳定.tomcat作为应用容器当然可以有linux版本的tomcat.在linux上安装tomcat的方式也很简单,只需要运行脚本基本配 ...

  3. Jquery的一些方法

    $.trim(str);说明:去掉字符串首尾空格.

  4. CSS背景颜色透明

    { filter:alpha(opacity=50); -moz-opacity:0.5; -khtml-opacity: 0.5; opacity: 0.5; } 兼容大部分主流浏览器 filter ...

  5. CI CD概念

    CI:持续集成 Continuous Integration CD:持续发布 Continuous Delivery (完)

  6. 每天一个LINUX命令(pwd)

    每天一个LINUX命令(pwd) 基本信息 pwd: /bin/pwd,显示当前路径的绝对路径         语法:pwd 应用程序位置     which pwd PWD作用 pwd --help ...

  7. html2canvas截图问题,图片跨域导致截图空白

    年前的一个项目,要做一个H5截屏分享的功能,使用的是html2canvas插件,截图功能是实现了,但是跨域的图片死活不出来, 经过几天谷歌百度和不断的尝试,终于找到解决办法了,一共经历了让人心力憔悴的 ...

  8. 二分查找&二叉排序树

    首先我们先来复习一下二分查找的算法 对于正向序列的二分查找 递归实现: bool binary_search(vector<int> &sort_arry,int begin,in ...

  9. 仿Neo4j里的知识图谱,利用d3+vue开发的一个网络拓扑图

    项目需要画一个类似知识图谱的节点关系图. 一开始用的是echart画的. 根据https://gallery.echartsjs.com/editor.html?c=xH1Rkt3hkb,成功画出简单 ...

  10. 普通平衡树学习笔记之Splay算法

    前言 今天不容易有一天的自由学习时间,当然要用来"学习".在此记录一下今天学到的最基础的平衡树. 定义 平衡树是二叉搜索树和堆合并构成的数据结构,它是一 棵空树或它的左右两个子树的 ...