python 找到装饰器_python装饰器大详解

1.作用域

在python中,作用域分为两种:全局作用域和局部作用域。

全局作用域是定义在文件级别的变量,函数名。而局部作用域,则是定义函数内部。

关于作用域,我要理解两点:a.在全局不能访问到局部定义的变量 b.在局部能够访问到全局定义的变量,但是不能修改全局定义的变量(当然有方法可以修改)

下面我们来看看下面实例:

x = 1

deffunx():

x= 10

print(x) #打印出10

funx()print(x) #打印出1

如果局部没有定义变量x,那么函数内部会从内往外开始查找x,如果没有找到,就会报错

x = 1

deffunx():print(x) #打印出1

funx()print(x) #打印出1

x = 1

deffunx():deffunc1():print(x) #打印出1

func1()

funx()print(x) #打印出1

因此,关于作用域的问题,只需要记住两点就行:全局变量能够被文件任何地方引用,但修改只能在全局进行操作;如果局部没有找到所需的变量,就会往外进行查找,没有找到就会报错。

2.高级函数

我们知道,函数名其实就是指向一段内存空间的地址,既然是地址,那么我们可以利用这种特性来。

a函数名可以作为一个值

defdelete(ps):importos

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 inline:

line= line.replace(delelemetns,'')

f_write.write(line)

os.remove(filename)

os.rename('tmp.txt',filename)defadd(ps):

filename= ps[-1]

addelemetns= ps[1]

with open(filename,'a', encoding='utf-8') as fp:

fp.write("\n", addelemetns)defmodify(ps):importos

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 inline:

line= line.replace(modify_elemetns, '')

f_write.write(line)

os.remove(filename)

os.rename('tmp.txt', filename)defsearch(cmd):

filename= cmd[-1]

pattern= cmd[1]

with open(filename,'r', encoding="utf-8") as f:for line inf:if pattern inline:print(line, end="")else:print("没有找到")

dic_func={'delete': delete, 'add': add, 'modify': modify, 'search': search}whileTrue:

inp= input("请输入您要进行的操作:").strip()if notinp:continuecmd_1=inp.split()

cmd=cmd_1[0]if cmd indic_func:

dic_func[cmd](cmd_1)else:print("Error")

将函数作为字典值,实现文本数据的增删查改操作

b.函数名可以作为返回值

defouter():definner():pass

returninner

s=outer()print(s)######输出结果为#######

.inner at 0x000000D22D8AB8C8>

c..函数名可以作为一个参数

defindex():print("index func")defouter(index):

s=index

s()

outer(index)######输出结果#########

index func

所以满足上面两个条件中的一个,都可以称为高级函数.

3.闭包函数

闭包函数必须满足两个条件:1.函数内部定义的函数 2.包含对外部作用域而非全局作用域的引用

下面通过一些实例来说明闭包函数:

实例一:以下仅仅在函数内部定义了一个函数,但并非闭包函数.

defouter():definner():print("inner func excuted")

inner()#调用执行inner()函数

print("outer func excuted")

outer()#调用执行outer函数

####输出结果为##########

inner func excuted

outer func excuted

实例二:以下在函数内部定义了一个函数,而且还引用了一个外部变量x,那么这个是闭包函数么?答案:不是

x = 1defouter():

definner():print("x=%s" %x) #引用了一个非inner函数内部的变量

print("inner func excuted")

inner()#执行inner函数

print("outer func excuted")

outer()#####输出结果########

x=1inner func excuted

outer func excuted

在回头来看看对闭包函数的定义,是不是两条都满足?聪明的你,一定发现不满足第二条.对,这里的变量x,是属于全局变量,而非外部作用于域的变量。再来看看下面例子:

defouter():

x= 1

definner():print("x=%s" %x)print("inner func excuted")

inner()print("outer func excuted")

outer()#####输出结果#########

x=1inner func excuted

outer func excuted

显然,上面实例满足闭包函数的条件。现在,你应该清楚,作为一个闭包函数,必须得满足上述的两个条件,缺一不可。但是,一般情况下,我们都会给闭包函数返回一个值.这里先不说为什么.在接下来的内容中,你会看到这个返回值的用途.

defouter():

x= 1

definner():print("x=%s" %x)print("inner func excuted")print("outer func excuted")returninner # 返回内部函数名

outer()

现在我们来抽象的定义一下闭包函数。它是函数和与其相关的引用环境组合而成的实体。在实现深约束时,需要创建一个能显式表示引用环境的东西,并将它与相关的子程序捆绑在一起,这样捆绑起成为闭包。在上面实例中,我们可以发现,闭包函数,它必须包含自己的函数以及一个外部变量才能真正称得上是一个闭包函数。如果没有一个外部变量与其绑定,那么這个函数不能算得上是闭包函数。

那么怎么知道一个闭包函数有多少个外部引用变量呢?看看下面代码.

defouter():

x= 1y= 2

definner():print("x= %s" %x)print("y= %s" %y)print(inner.__closure__)returninner

outer()######输出结果#######

(, )

结果表明,在inner内部,引用了两个外部局部变量。如果引用的是非局部变量,那么这里输出的为None.

闭包函数的特点:1.自带作用域 2.延迟计算

那么闭包函数有什么作用呢?我们清楚的知道,闭包函数在定义时,一定会绑定一个外部环境。這个整体才能算的上是一个闭包函数,那么我们可以利用这个绑定特性,来完成某些特殊的功能。

实例三:根据传入的URL,来下载页面源码

from urllib.request importurlopendefindex(url)defget()returnurlopen(url).read()returnget

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.无参装饰器

有如下实例,我们需要计算一下代码执行的时间。

importtime, randomdefindex():

time.sleep(random.randrange(1, 5))print("welcome to index page")

根据装饰器的特点,我们不能对index()进行任何修改,而且调用方式也不能变。这时候,我们就可以使用装饰器来完成如上功能.

importtime, randomdef outer(func): #将index的地址传递给func

definner():

start_time=time.time()

func()#fun = index 即func保存了外部index函数的地址

end_time =time.time()print("运行时间为%s"%(end_time -start_time))return inner #返回inner的地址

defindex():

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的地址

下面来说说一些其他情况的实例。

如果被装饰的函数有返回值

deftimmer(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))returnresreturnwrapperdefhome(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)是一样的。

如果一个函数被多个装饰器装饰,那么执行顺序是怎样的。

importtimeimportrandomdeftimmer(func):defwrapper():

start_time=time.time()

func()

stop_time=time.time()print('run time is %s' %(stop_time-start_time))returnwrapperdefauth(func):defdeco():

name=input('name:')

password=input('password:')if name == 'egon' and password == '123':print('login successful')

func()#wrapper()

else:print('login err')returndeco

@auth#index = auth(timmer(index))

@timmer #index = timmer(index)

defindex():

time.sleep(3)print('welecome to index page')

index()

View Code

实验结果表明,多个装饰器装饰一个函数,其执行顺序是从下往上。

关于装饰器,还有一些高级用法,有兴趣的可以自己研究研究。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值