Python中被装饰器修饰的函数,解析后会生成一个参数是被修饰函数的装饰器函数对象,可以调用,可以接受传参(如果被修饰的函数定义了参数),实际调用的时候,尽管代码里值写了被修饰的函数,被调用的却是最终生成的复杂对象。对于这个对象,目前有以下几点疑问未得到确认:

1、最后生成的对象在被调用时,参数传递的顺序是什么样的?

2、装饰器函数中如果和被修饰函数中有同名变量,会产生什么结果?

3、如果装饰器函数返回对象是一个与获得的参数对象无关的可调用对象,会对运行结果产生什么影响?

1、运行以下代码:

 def addSpan(fn):
print "addSpan executed"
def n(*args):
print "spam, spam, spam"
print "args of func n:", str(args)
return fn(*args)
return n @addSpan
def useful(a, b):
print "args of useful:", (a, b)
print a**2 + b**2 def synonym(a, b):
print a**2 + b**2 if __name__ == '__main__':
useful(3, 4)

运行结果是:

addSpan executed
spam, spam, spam
args of func n: (3, 4)
args of useful: (3, 4)
25
[Finished in 0.1s]

没有异步逻辑,所有函数都是顺序执行的。通过结果可以看出,在运行被修饰的函数时,装饰器函数内部的函数n被首先执行,而它的参数就是useful被调用时传入的参数,然后就是按照n被声明时的顺序,逐行执行。fn最后执行,也就是被修饰的函数。

•对于第一行的装饰器函数的输出,现在还有些疑问,将在后面验证。

根据以上运行结果可以得出结论:装饰器的作用是构造一个可以访问到被修饰函数的函数对象(闭包?),这个函数对象接收被修饰函数被调用时的参数作为运行参数,而执行的逻辑则是另外的逻辑,甚至可以与被修饰函数完全无关。在上述代码中,即使注释掉第6行的return fn(*args),程序仍旧可以正常运行,运行结果如下:

addSpan executed
spam, spam, spam
args of func n: (3, 4)
[Finished in 0.1s]

可以确认,装饰器与被修饰的函数逻辑可以完全无关。

运行时参数会直接传给装饰器函数返回的可调用对象,被修饰函数是否会被执行视装饰器的内部函数逻辑而定,不存在必须执行被修饰函数的限制。

2、运行以下代码:

 def addSpan(fn):
print "addSpan executed"
t = "Hello " * 3 + "blablabla"
def n(*args):
print "spam, spam, spam"
print "args of func n:", str(args)
print "t of n:", t
return fn(*args)
return n @addSpan
def useful(a, b):
print "args of useful:", (a, b)
print a**2 + b**2
t = a * b
print 't in useful:', t if __name__ == '__main__':
useful(3, 4)

运行结果:

addSpan executed
spam, spam, spam
args of func n: (3, 4)
t of n: Hello Hello Hello blablabla
args of useful: (3, 4)
25
t in useful: 12
[Finished in 0.1s]

可以看出,装饰器函数中的变量与被修饰函数中的同名变量互不干扰。

经过验证,分别注释掉第3行的t声明或者第15行的t声明,都一样会在各自的函数中报出找不到变量的错误。由此可知,装饰器函数的变量域和被修饰函数的变量域不存在继承关系。

更进一步,如果装饰器函数和被修饰函数使用同一个全局变量,会产生什么结果?

运行如下代码:

 def addSpan(fn):
global T
T = "Hello " * 3 + "blablabla"
print 'id of global variable T:', id(T) print "addSpan executed"
def n(*args):
T = str(args)
print "id of T in n:", id(T)
print "spam, spam, spam"
print "args of func n:", str(args)
print "T in n:", T
return fn(*args)
return n @addSpan
def useful(a, b):
print "args of useful:", (a, b)
print a**2 + b**2
T = a * b
print 'T in useful:', T
print "id of T in useful:", id(T) @addSpan
def otherFunc():
print "otherFunc executed"
print "T in otherFunc:", T
print "id of T in otherFunc:", id(T) if __name__ == '__main__':
useful(3, 4)
print ''
otherFunc()

运行结果如下:

id of global variable T: 4513798704
addSpan executed
id of global variable T: 4513798896
addSpan executed
id of T in n: 4513827168
spam, spam, spam
args of func n: (3, 4)
T in n: (3, 4)
args of useful: (3, 4)
25
T in useful: 12
id of T in useful: 140312483777024 id of T in n: 4513769432
spam, spam, spam
args of func n: ()
T in n: ()
otherFunc executed
T in otherFunc: Hello Hello Hello blablabla
id of T in otherFunc: 4513798896
[Finished in 0.1s]

通过观察不同函数里变量T的id,可以发现关于全局变量的猜想似乎没有太大意义,每次T被重新赋值时,会重新生成变量T,只有在T没有被声明过的时候,才会寻找全局变量。

3、如果装饰器函数的返回对象是与被修饰函数无关的对象,会有什么结果?

上文1中已经解释了一部分可能产生的结果:只要语法正确,即使不执行业务逻辑,程序也不会出错。

换个思路,如果装饰器函数有返回对象,但是执行另外的逻辑,会发生什么?

运行以下代码:

 def addSpan(fn):
print "addSpan executed"
def n(*args):
print "spam, spam, spam"
print "args of func n:", str(args)
return sorted(args) // 不调用参数fn
return n @addSpan
def useful(a, b):
print "args of useful:", (a, b)
print a**2 + b**2 if __name__ == '__main__':
useful(3, 4)

运行结果:

addSpan executed
spam, spam, spam
args of func n: (3, 4)
[Finished in 0.1s]

正常结束,除了没有运行被修饰函数本身的逻辑。

换个写法:

def otherFunc(*args):
print "otherFunc executed"
print "args in otherFunc:", str(args) def addSpan(fn):
print "addSpan executed"
return otherFunc // 运行另外函数 @addSpan
def useful(a, b):
print "args of useful:", (a, b)
print a**2 + b**2 if __name__ == '__main__':
useful(3, 4)

运行结果:

addSpan executed
otherFunc executed
args in otherFunc: (3, 4)
[Finished in 0.1s]

一切正常,被修饰的函数的作用类似另一个函数otherFunc的别名。

经过以上验证,可以确定,装饰器函数如果执行与被修饰函数无关的函数,程序运行不会出错,只是被修饰函数的逻辑不会被执行,此时被修饰函数的仅是作为装饰器函数的程序运行入口而存在。

• 另外,据官方文档,在解析被装饰器修饰的函数时,每个被修饰的函数在经过编译之后,内容将保持被解析时的状态不会被改变,直到程序被重新编译。这一点上面2的结论也可以解释:变量域固定且互相隔离。

以下代码关于变量t的变化也可以说明:

 from time import time

 def addSpan(fn):
print "addSpan executed"
t = time()
def n(*args):
print "now is %f" % t
print "spam, spam, spam"
print "args of func n:", str(args)
return fn(*args)
return n @addSpan
def useful(a, b):
print "args of useful:", (a, b)
print a**2 + b**2 @addSpan
def otherFunc():
print "otherFunc executed" if __name__ == '__main__':
useful(3, 4)
print ''
useful(4, 6)
print ''
otherFunc()

运行结果:

addSpan executed
addSpan executed
now is 1470139094.499294
spam, spam, spam
args of func n: (3, 4)
args of useful: (3, 4)
25 now is 1470139094.499294
spam, spam, spam
args of func n: (4, 6)
args of useful: (4, 6)
52 now is 1470139094.499297
spam, spam, spam
args of func n: ()
otherFunc executed
[Finished in 0.1s]

两次调用useful的 t 的值都相同,又与调用otherFunc时 t 的值不同。

• 在上面的运行结果中也可以发现,即使被修饰的函数没有执行,在运行时,装饰器函数的print记录依然出现了,是否可以理解为编译时,装饰器函数已经被执行,被存放在内存中等待被调用的,只是装饰器函数返回的可调用对象。

关于Python的装饰器(2)的更多相关文章

  1. Python各式装饰器

    Python装饰器,分两部分,一是装饰器本身的定义,一是被装饰器对象的定义. 一.函数式装饰器:装饰器本身是一个函数. 1.装饰函数:被装饰对象是一个函数 [1]装饰器无参数: a.被装饰对象无参数: ...

  2. Python札记 -- 装饰器补充

    本随笔是对Python札记 -- 装饰器的一些补充. 使用装饰器的时候,被装饰函数的一些属性会丢失,比如如下代码: #!/usr/bin/env python def deco(func): def ...

  3. python基础——装饰器

    python基础——装饰器 由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数. >>> def now(): ... print('2015-3-25 ...

  4. 【转】详解Python的装饰器

    原文链接:http://python.jobbole.com/86717/ Python中的装饰器是你进入Python大门的一道坎,不管你跨不跨过去它都在那里. 为什么需要装饰器 我们假设你的程序实现 ...

  5. 两个实用的Python的装饰器

    两个实用的Python的装饰器 超时函数 这个函数的作用在于可以给任意可能会hang住的函数添加超时功能,这个功能在编写外部API调用 .网络爬虫.数据库查询的时候特别有用 timeout装饰器的代码 ...

  6. python 基础——装饰器

    python 的装饰器,其实用到了以下几个语言特点: 1. 一切皆对象 2. 函数可以嵌套定义 3. 闭包,可以延长变量作用域 4. *args 和 **kwargs 可变参数 第1点,一切皆对象,包 ...

  7. 理解Python中的装饰器//这篇文章将python的装饰器来龙去脉说的很清楚,故转过来存档

    转自:http://www.cnblogs.com/rollenholt/archive/2012/05/02/2479833.html 这篇文章将python的装饰器来龙去脉说的很清楚,故转过来存档 ...

  8. python基础—装饰器

    python基础-装饰器 定义:一个函数,可以接受一个函数作为参数,对该函数进行一些包装,不改变函数的本身. def foo(): return 123 a=foo(); b=foo; print(a ...

  9. 详解Python的装饰器

    Python中的装饰器是你进入Python大门的一道坎,不管你跨不跨过去它都在那里. 为什么需要装饰器 我们假设你的程序实现了say_hello()和say_goodbye()两个函数. def sa ...

  10. 关于python的装饰器(初解)

    在python中,装饰器(decorator)是一个主要的函数,在工作中,有了装饰器简直如虎添翼,许多公司面试题也会考装饰器,而装饰器的意思又很难让人理解. python中,装饰器是一个帮函数动态增加 ...

随机推荐

  1. 解决MySQL新建用户后无法登录问题

    在PHPMyAdmin里创建了一个新的用户,并且创建了密码,但是却一直无法使用这个账户登录到MySQL里. 解决过程分享给大家~ 1.以root身份登录mysql 2.删除MySQL中默认存在一个用户 ...

  2. Kali-linux无线网络嗅探工具Kismet

    如果要进行无线网络渗透测试,则必须先扫描所有有效的无线接入点.刚好在Kali Linux中,提供了一款嗅探无线网络工具Kismet.使用该工具可以测量周围的无线信号,并查看所有可用的无线接入点.本节将 ...

  3. [转]谈谈关于MVP模式中V-P交互问题

    在差不多两年的时间内,我们项目组几十来号人都扑在一个项目上面.这是一个基于微软SCSF(Smart Client Software Factory)的项目,客户端是墨尔本一家事业单位.前两周,我奉命负 ...

  4. 近十年one-to-one最短路算法研究整理

    前言:针对单源最短路算法,目前最经典的思路即标号算法,以Dijkstra算法和Bellman-Ford算法为根本演进了各种优化技术和算法.针对复杂网络,传统的优化思路是在数据结构和双向搜索上做文章,或 ...

  5. python 装饰器 传递参数简单案例

    def debug(func): def wrapper(*args, **kwargs): # 指定宇宙无敌参数 print "[DEBUG]: enter {}()".form ...

  6. CC2640R2F&TI-RTOS 拿到 TI CC2640R2F 开发板 第一件事就是移植串口驱动,重定向 printf

    /* * board_uart.c * * Created on: 2018年7月3日 * Author: admin */ #include "board_uart.h" #in ...

  7. mysql如何把一个表直接拷贝到一个新的表

    一:在新表已经建立好的情况下 1,拷贝所有的字段 insert into new_table select * from old_table 2,拷贝部分字段表 insert into new_tab ...

  8. spring定时任务注解@Scheduled的记录

    spring 定时任务@Scheduled 转自https://www.cnblogs.com/0201zcr/p/5995779.html 1.配置文件 <?xml version=" ...

  9. 13JavaScript运算符

    运算符 = 用于给 JavaScript 变量赋值. 算术运算符 + 用于把值加起来 实例 指定变量值,并将值相加: y=5; z=2; x=y+z; 在以上语句执行后,x 的值是:7 1.JavaS ...

  10. Linux网卡配置文件 参数详解

    之所以弄这玩意儿是图个清晰方便,最近这段时间弄了好十来次虚拟机网络了ubuntu,centos,rhat7各种折腾,其实把网上各种命令行下的攻略折腾最后关键无非都是对/etc/sysconfig/ne ...