Jaglawz: 听讲Python一切都是对象,是吗?python
Pylego: 是的,像函数也是对象。闭包
Jaglawz: 那么函数也能够有本身的属性了?app
Pylego: 固然,像下面这样写是能够的:函数
def foo(): print('I am foo') def bar(): print('I am bar') foo.bar = bar foo.bar()
Jaglawz: 这都行,那是否是函数也能够像普通对象同样看成参数传递也能够看成对象来返回?code
Pylego: 是的,好比下面的用法:对象
def deco(func): string = 'I am deco' def wrapper(): print(string) func() return wrapper def foo(): print('I am foo') foo = deco(foo) foo() """ 输出: I am deco Iam foo """
Jaglawz: 好吧,看成参数传递和返回我理解了,可是我对wrapper函数的print(string)这个string的查找感到迷惑。内存
Pylego: 不错嘛!这都被你看出来了,那你知道Python做用域的LEGB原则吗?作用域
Jaglawz: 我知道是知道能够我就是对那个E(Enclosing)做用域不是很理解。开发
Pylego: 那就对了,你能够在刚才代码的基础上运行下面的代码:string
print(foo.__closure__) # 输出:(<cell at 0x7fc50f45afd8: function object at 0x7fc50f4168c0>, <cell at 0x7fc50f45aec0: str object at 0x7fc50c065fc0>)
Jaglawz: 咦,这两个内存地址是啥家伙?
Pyelgo: 这就是wrapper函数引用的外层函数(就是deco函数啦)的两个变量:string和func啊!
Jaglawz: 也就是说内层函数(在本例中就是wrapper啦)会把外层函数(在本例用就是deco啦)做用域里面的对象放到__closure__属性里,以供本身查找?
Pylego: 是的,可是不是全部外层函数做用域的对象都会放到内层函数的__closure__属性里,仅限本身用到的,这个__closure__就是enclosing做用域啦!
Jaglawz: 原来enclosing做用域是这样的,明白了。
Pyelgo: 若是内部函数引用到外层函数做用域的对象,这个内部函数就称为闭包。
Jaglawz: 原来闭包就是这家伙,很简单嘛!
Jaglawz: 咦,我想到内部函数有一个妙用,你看看是否是这样啊,好比说我想输出一个函数的运行时间又不想去破坏这个函数的代码,是否是能够这样写:
import time def time_machine(func): def wrapper(*args, **kwargs): start_time = time.time() func(*args, **kwargs) print(u'共耗时: %s秒' % (time.time()-start_time)) return wrapper def foo(): time.sleep(3) foo = time_machine(foo) foo()
Pylego: 你这智商要冲出宇宙的节奏啊!可是Python的开发者早就想到每次foo = time_machine(foo)很麻烦,特意为你准备了语法糖,来接糖:
import time def time_machine(func): def wrapper(*args, **kwargs): start_time = time.time() func(*args, **kwargs) print(u'共耗时: %s秒' % (time.time()-start_time)) return wrapper @time_machine def foo(): time.sleep(3) """ 也就是说: @time_machine def foo(): pass 至关于: foo = time_machine(foo),这里是重点,这里是重点,这里是重点,之后的谈话能不能理解就看你对这个语句可理解! """ foo()
Pylego: time_machine就是装饰器(decorator),名字起得多形象啊,装饰函数嘛!
Jaglawz: 你这么一说,我就以为装饰器咋这么简单呢!问题是我看到不少人的装饰器还带参数,还有人用类当装饰器,这又是咋回事呢?
Pylego: 你问题咋恁些?我先吃个饭,下次有空再聊哈!