python装饰器大详解
1.作用域
在python中,作用域分为两种:全局作用域和局部作用域。
全局作用域是定义在文件级别的变量,函数名。而局部作用域,则是定义函数内部。
关于作用域,我要理解两点:a.在全局不能访问到局部定义的变量 b.在局部能够访问到全局定义的变量,但是不能修改全局定义的变量(当然有方法可以修改)
下面我们来看看下面实例:
x = 1
def funx():
x = 10
print(x) # 打印出10 funx()
print(x) # 打印出1
如果局部没有定义变量x,那么函数内部会从内往外开始查找x,如果没有找到,就会报错
x = 1
def funx():
print(x) # 打印出1 funx()
print(x) # 打印出1
x = 1
def funx():
def func1():
print(x) # 打印出1
func1() funx()
print(x) # 打印出1
因此,关于作用域的问题,只需要记住两点就行:全局变量能够被文件任何地方引用,但修改只能在全局进行操作;如果局部没有找到所需的变量,就会往外进行查找,没有找到就会报错。
2.高级函数
我们知道,函数名其实就是指向一段内存空间的地址,既然是地址,那么我们可以利用这种特性来。
a函数名可以作为一个值
def delete(ps):
import os
filename = ps[-1]
delelemetns = ps[1]
with open(filename, encoding='utf-8') as f_read,\
open('tmp.txt', 'w', encoding='utf-8') as f_write:
for line in iter(f_read.readline, ''):
if line != '\n': # 处理非空行
if delelemetns in line:
line = line.replace(delelemetns,'')
f_write.write(line)
os.remove(filename)
os.rename('tmp.txt',filename) def add(ps):
filename = ps[-1]
addelemetns = ps[1]
with open(filename, 'a', encoding='utf-8') as fp:
fp.write("\n", addelemetns) def modify(ps):
import os
filename = ps[-1]
modify_elemetns = ps[1]
with open(filename, encoding='utf-8') as f_read, \
open('tmp.txt', 'w', encoding='utf-8') as f_write:
for line in iter(f_read.readline, ''):
if line != '\n': # 处理非空行
if modify_elemetns in line:
line = line.replace(modify_elemetns, '')
f_write.write(line)
os.remove(filename)
os.rename('tmp.txt', filename) def search(cmd):
filename = cmd[-1]
pattern = cmd[1]
with open(filename, 'r', encoding="utf-8") as f:
for line in f:
if pattern in line:
print(line, end="")
else:
print("没有找到") dic_func ={'delete': delete, 'add': add, 'modify': modify, 'search': search} while True:
inp = input("请输入您要进行的操作:").strip()
if not inp:
continue
cmd_1 = inp.split()
cmd = cmd_1[0]
if cmd in dic_func:
dic_func[cmd](cmd_1)
else:
print("Error")
将函数作为字典值,实现文本数据的增删查改操作
b.函数名可以作为返回值
def outer():
def inner():
pass
return inner s = outer()
print(s) ######输出结果为#######
<function outer.<locals>.inner at 0x000000D22D8AB8C8>
c..函数名可以作为一个参数
def index():
print("index func") def outer(index):
s = index
s() outer(index) ######输出结果######### index func
所以满足上面两个条件中的一个,都可以称为高级函数.
3.闭包函数
闭包函数必须满足两个条件:1.函数内部定义的函数 2.包含对外部作用域而非全局作用域的引用
下面通过一些实例来说明闭包函数:
实例一:以下仅仅在函数内部定义了一个函数,但并非闭包函数.
def outer():
def inner():
print("inner func excuted")
inner() # 调用执行inner()函数
print("outer func excuted")
outer() # 调用执行outer函数 ####输出结果为##########
inner func excuted
outer func excuted
实例二:以下在函数内部定义了一个函数,而且还引用了一个外部变量x,那么这个是闭包函数么?答案:不是
x = 1
def outer():
def inner():
print("x=%s" %x) # 引用了一个非inner函数内部的变量
print("inner func excuted")
inner() # 执行inner函数
print("outer func excuted") outer()
#####输出结果########
x=1
inner func excuted
outer func excuted
在回头来看看对闭包函数的定义,是不是两条都满足?聪明的你,一定发现不满足第二条.对,这里的变量x,是属于全局变量,而非外部作用于域的变量。再来看看下面例子:
def outer():
x = 1
def inner():
print("x=%s" %x)
print("inner func excuted")
inner()
print("outer func excuted") outer() #####输出结果#########
x=1
inner func excuted
outer func excuted
显然,上面实例满足闭包函数的条件。现在,你应该清楚,作为一个闭包函数,必须得满足上述的两个条件,缺一不可。但是,一般情况下,我们都会给闭包函数返回一个值.这里先不说为什么.在接下来的内容中,你会看到这个返回值的用途.
def outer():
x = 1
def inner():
print("x=%s" %x)
print("inner func excuted")
print("outer func excuted")
return inner # 返回内部函数名 outer()
现在我们来抽象的定义一下闭包函数。它是函数和与其相关的引用环境组合而成的实体。在实现深约束时,需要创建一个能显式表示引用环境的东西,并将它与相关的子程序捆绑在一起,这样捆绑起成为闭包。在上面实例中,我们可以发现,闭包函数,它必须包含自己的函数以及一个外部变量才能真正称得上是一个闭包函数。如果没有一个外部变量与其绑定,那么這个函数不能算得上是闭包函数。
那么怎么知道一个闭包函数有多少个外部引用变量呢?看看下面代码.
def outer():
x = 1
y = 2 def inner():
print("x= %s" %x)
print("y= %s" %y) print(inner.__closure__)
return inner outer() ######输出结果#######
(<cell at 0x000000DF9EA965B8: int object at 0x000000006FC2B440>, <cell at 0x000000DF9EA965E8: int object at 0x000000006FC2B460>)
结果表明,在inner内部,引用了两个外部局部变量。如果引用的是非局部变量,那么这里输出的为None.
闭包函数的特点:1.自带作用域 2.延迟计算
那么闭包函数有什么作用呢?我们清楚的知道,闭包函数在定义时,一定会绑定一个外部环境。這个整体才能算的上是一个闭包函数,那么我们可以利用这个绑定特性,来完成某些特殊的功能。
实例三:根据传入的URL,来下载页面源码
from urllib.request import urlopen def index(url)
def get()
return urlopen(url).read()
return get python = index("http://www.python.org") # 返回的是get函数的地址
print(python()) # 执行get函数《并且将返回的结果打印出来
baidu = index("http://www.baidu.com")
print(baidu())
有人可以会说,这个不满足闭包函数的条件啊!我没有引用非全局的外部变量啊。其实并非如此,给,我们之前说过,只要在函数内部的变量都属于函数。那么我在index(url),这个url也属于函数内部,只不过我们省略一步而已,所以上面那个函数也是闭包函数。
4.装饰器
有了以上基础,对于装饰器就好理解了.
装饰器:外部函数传入被装饰函数名,内部函数返回装饰函数名。
特点:1.不修改被装饰函数的调用方式 2.不修改被装饰函数的源代码
a.无参装饰器
有如下实例,我们需要计算一下代码执行的时间。
import time, random def index():
time.sleep(random.randrange(1, 5))
print("welcome to index page")
根据装饰器的特点,我们不能对index()进行任何修改,而且调用方式也不能变。这时候,我们就可以使用装饰器来完成如上功能.
import time, random def outer(func): # 将index的地址传递给func
def inner():
start_time = time.time()
func() # fun = index 即func保存了外部index函数的地址
end_time = time.time()
print("运行时间为%s"%(end_time - start_time))
return inner # 返回inner的地址 def index():
time.sleep(random.randrange(1, 5))
print("welcome to index page") index = outer(index) # 这里返回的是inner的地址,并重新赋值给index index()
装饰器实现计时
但是,有些情况,被装饰的函数需要传递参数进去,有些函数又不需要参数,那么如何来处理这种变参数函数呢?下面来看看有参数装饰器的使用情况.
b.有参装饰器
def outer(func): # 将index的地址传递给func
def inner(*args, **kwargs):
start_time = time.time()
func(*args, **kwargs) # fun = index 即func保存了外部index函数的地址
end_time = time.time()
print("运行时间为%s"%(end_time - start_time))
return inner # 返回inner的地址
下面来说说一些其他情况的实例。
如果被装饰的函数有返回值
def timmer(func):
def wrapper(*args,**kwargs):
start_time = time.time()
res=func(*args,**kwargs) #res来接收home函数的返回值
stop_time=time.time()
print('run time is %s' %(stop_time-start_time))
return res
return wrapper def home(name):
time.sleep(random.randrange(1,3))
print('welecome to %s HOME page' %name)
return 123123123123123123123123123123123123123123
这里补充一点,加入我们要执行被装饰后的函数,那么应该是如下调用方式:
home = timmer(home) # 等式右边返回的是wrapper的内存地址,再将其赋值给home,这里的home不在是原来的的那个函数,而是被装饰以后的函数了。像home = timmer(home)这样的写法,python给我们提供了一个便捷的方式------语法糖@.以后我们再要在被装饰的函数之前写上@timmer,它的效果就和home = timmer(home)是一样的。
如果一个函数被多个装饰器装饰,那么执行顺序是怎样的。
import time
import random def timmer(func):
def wrapper():
start_time = time.time()
func()
stop_time=time.time()
print('run time is %s' %(stop_time-start_time))
return wrapper
def auth(func):
def deco():
name=input('name: ')
password=input('password: ')
if name == 'egon' and password == '':
print('login successful')
func() #wrapper()
else:
print('login err')
return deco @auth # index = auth(timmer(index))
@timmer # index = timmer(index)
def index(): time.sleep(3)
print('welecome to index page') index()
实验结果表明,多个装饰器装饰一个函数,其执行顺序是从下往上。
关于装饰器,还有一些高级用法,有兴趣的可以自己研究研究。
python装饰器大详解的更多相关文章
- python装饰器学习详解-函数部分
本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理 最近阅读<流畅的python>看见其用函数写装饰器部分写的很好,想写一些自己的读书笔记. ...
- python装饰器使用详解
装饰器 '''装饰器:就是闭包(闭包的一个应用场景) -- 把要被装饰的函数作为外层函数的参数通过闭包操作后返回一个替代版函数 优点: -- 丰富了原有函数的功能 -- 提高了程序的可拓展性''' 开 ...
- 进阶Python:装饰器 全面详解
进阶Python:装饰器 前言 前段时间我发了一篇讲解Python调试工具PySnooper的文章,在那篇文章开始一部分我简单的介绍了一下装饰器,文章发出之后有几位同学说"终于了解装饰器的用 ...
- python 函数及变量作用域及装饰器decorator @详解
一.函数及变量的作用 在python程序中,函数都会创建一个新的作用域,又称为命名空间,当函数遇到变量时,Python就会到该函数的命名空间来寻找变量,因为Python一切都是对象,而在命名空间中 ...
- (十)装饰器模式详解(与IO不解的情缘)
作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. LZ到目前已经写了九个设计模 ...
- 涉及模式之 装饰器模式详解(与IO不解的情缘)
作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. LZ到目前已经写了九个设计模 ...
- Java 装饰器模式详解
转载请注明出处:http://blog.csdn.net/zhaoyanjun6/article/details/56488020 前言 在上面的几篇文章中,着重介绍了Java 中常见的 IO 相关知 ...
- Python装饰器详解
python中的装饰器是一个用得非常多的东西,我们可以把一些特定的方法.通用的方法写成一个个装饰器,这就为调用这些方法提供一个非常大的便利,如此提高我们代码的可读性以及简洁性,以及可扩展性. 在学习p ...
- python装饰器(docorator)详解
引言: 装饰器是python面向对象编程三大器之一,另外两个迭代器.生成器只是我现在还没有遇到必须使用的场景,等确实需要用到的时候,在补充资料:装饰器在某些场景真的是必要的,比如定义了一个类或者一个函 ...
随机推荐
- Linux基础(7)
Linux 基础(7) 一.内存的监控(free) free -m 以单位为MB的方式查看内存的使用情况(free命令读取的文件是/proc/meminfo) total:是指计算机安装的内存总量 u ...
- C#中 dynamic 关键字
所有表达式都能隐式的转换成dynamic,因为所有的表达式最终都能生成从Object派生出的类型. ; int b = a; //隐式转换错误 int b2 = (int)a; ; int b3 ...
- PHP获取Post的原始数据方法小结(POST无变量名)
From : http://blog.csdn.net/hotdigger/article/details/6456240 一般我们都用$_POST或$_REQUEST两个预定义变量来接收POST ...
- mui开发app之自定义事件以更新其他页内容
我之前做过jquery mobile的开发,那还是前年的事情 在jquery mobile中,由于页面是存储在div[data-role=page]的dom中(jqmobile通过对data-role ...
- 蓝桥杯-搭积木-java
/* (程序头部注释开始) * 程序的版权和版本声明部分 * Copyright (c) 2016, 广州科技贸易职业学院信息工程系学生 * All rights reserved. * 文件名称: ...
- css3隐藏导航栏总结
display:none隐藏display:block显示 1.通过hover显示时隐藏元素必须与触发元素存在着关系(父子,兄弟)才能生效,所以推荐用JS完成比较好. 2.隐藏部分的属性直接写在hov ...
- 设计模式的征途—5.原型(Prototype)模式
相信大多数的人都看过<西游记>,对孙悟空拔毛变出小猴子的故事情节应该都很熟悉.孙悟空可以用猴毛根据自己的形象复制出很多跟自己一模一样的小猴兵出来,其实在设计模式中也有一个类似的模式,我们可 ...
- 为什么非全站升级HTTPS不可?
升级HTTPS已经是大势所趋,但仍有大量互联网企业犹豫是否要全站升级HTTPS,为此本文梳理了全站升级HTTPS与部分升级HTTPS的优劣势对比,来判断是否真的有必要进行全站HTTPS升级. HTTP ...
- css文字溢出隐藏,及强制断句
只显示一行文字,便溢出隐藏 text-overflow: ellipsis; white-place: nowrap; overflow: hidden; 显示 n 行文字后便溢出隐藏 displa ...
- 关于ie的h5 刷新和ctrl+5刷新 以及图标刷新的问题
最近在做一个表单验证,当表单失去焦点的时候触发错误提示. 可是..... 火狐 欧朋 刷新都没有问题,而在ie edge 用f5刷新的时候 都能记住之前的焦点 造成提示混乱. 问题算是解决: < ...