Jaglawz: 听讲 Python 一切都是对象,是吗?
Pylego: 是的,像函数也是对象。
Jaglawz: 那么函数也可以有自己的属性了?
Pylego: 当然,像下面这样写是可以的:
- def foo():print('I am foo')def bar():print('I am bar')
- foo.bar = bar
- foo.bar()
Jaglawz: 这都行,那是不是函数也可以像普通对象一样当作参数传递也可以当作对象来返回?
Pylego: 是的,比如下面的用法:
- def deco(func):string ='I am deco'
- def wrapper():print(string)
- func()returnwrapperdef foo():print('I am foo')
- foo = deco(foo)
- foo()"""
- 输出:
- I am deco
- Iam foo
- """
Jaglawz: 好吧,当作参数传递和返回我理解了,但是我对 wrapper 函数的 print(string) 这个 string 的查找感到迷惑。
Pylego: 不错嘛!这都被你看出来了,那你知道 Python 作用域的 LEGB 原则吗?
Python 中的作用域 Python 中,一个变量的作用域总是由在代码中被赋值的地方所决定的。 当 Python 遇到一个变量的话他会按照这样的顺序进行搜索: 本地作用域(Local)→当前作用域被嵌入的本地作用域(Enclosing locals)→全局 / 模块作用域(Global)→内置作用域(Built-in) Jaglawz: 我知道是知道可以我就是对那个 E(Enclosing)作用域不是很理解。
Pylego: 那就对了,你可以在刚才代码的基础上运行下面的代码:
- print(foo.__closure__)
- # 输出:(0x7fc50f45afd8:function object at0x7fc50f4168c0>, <cell at0x7fc50f45aec0:str object at0x7fc50c065fc0>)
Jaglawz: 咦,这两个内存地址是啥家伙?
Pyelgo: 这就是 wrapper 函数引用的外层函数(就是 deco 函数啦)的两个变量:string 和 func 啊!
Jaglawz: 也就是说内层函数(在本例中就是 wrapper 啦)会把外层函数(在本例用就是 deco 啦)作用域里面的对象放到
属性里,以供自己查找?
- __closure__
Pylego: 是的,但是不是所有外层函数作用域的对象都会放到内层函数的
属性里,仅限自己用到的,这个
- __closure__
就是 enclosing 作用域啦!
- __closure__
Jaglawz: 原来 enclosing 作用域是这样的,明白了。
Pyelgo: 如果内部函数引用到外层函数作用域的对象,这个内部函数就称为闭包。
Jaglawz: 原来闭包就是这家伙,很简单嘛!
当一个内嵌函数引用其外部作作用域的变量, 我们就会得到一个闭包. 总结一下, 创建一个闭包必须满足以下几点:
1. 必须有一个内嵌函数 2. 内嵌函数必须引用外部函数中的变量 3. 外部函数的返回值必须是内嵌函数
Jaglawz: 咦,我想到内部函数有一个妙用,你看看是不是这样啊,比如说我想输出一个函数的运行时间又不想去破坏这个函数的代码,是不是可以这样写:
- importtimedef time_machine(func):
- def wrapper(*args, **kwargs):start_time = time.time()
- func(*args, **kwargs)
- print(u'共耗时: %s秒'% (time.time()-start_time))returnwrapperdef foo():time.sleep(3)
- foo = time_machine(foo)
- foo()
Pylego: 你这智商要冲出宇宙的节奏啊!但是 Python 的开发者早就想到每次 foo = time_machine(foo) 很麻烦,特地为你准备了语法糖,来接糖:
- importtimedef time_machine(func):
- def wrapper(*args, **kwargs):start_time = time.time()
- func(*args, **kwargs)
- print(u'共耗时: %s秒'% (time.time()-start_time))returnwrapper@time_machine
- def foo():time.sleep(3)"""
- 也就是说:
- @time_machine
- def foo():
- pass
- 相当于: foo = time_machine(foo),这里是重点,这里是重点,这里是重点,以后的谈话能不能理解就看你对这个语句可理解!
- """foo()
Pylego: time_machine 就是装饰器 (decorator),名字起得多形象啊,装饰函数嘛!
Jaglawz: 你这么一说,我就觉得装饰器咋这么简单呢!问题是我看到很多人的装饰器还带参数,还有人用类当装饰器,这又是咋回事呢?
Jaglawz: 我经常看到有人的装饰器是带参数的,这又是咋回事呢?
Jaglawz: 我经常看到有人的装饰器是带参数的,这又是咋回事呢?
Pylego: 这个其实很简单的,你还记得上次我说
- @deco
- def foo():
- pass
- # 相当于: foo = deco(foo)
- # 那么
- @new_deco(*args,**kwargs)
- def bar():
- pass
- # 相当于: bar = new_deco(*args, **kwargs)(bar)
Jaglawz: 也就是说,new_deco 返回的是一个装饰器函数,然后再去装饰其他函数。那类装饰器又是怎么回事呢?
Pylego: 你知道 Python 的对象可以像函数一样调用吗?
- fromhashlibimportsha256class HashCache(object):
- def __init__(self):self.cache = {}def __call__(self, string):
- ifstringnot inself.cache:
- self.cache[string] = sha256(string).hexdigest()returnself.cache[string]
- hc = HashCahce()
- hc('foo')# 像函数一样调用hc对象hc('foo')
- hc('bar')
- Jaglawz: 如果是这样的话我就明白了。class FibCache(object):
- def __init__(self, func):self.func = func
- self.cache = {}def __call__(self, n):
- ifnnot inself.cache:
- self.cache[n] = self.func(n)returnself.cache[n]@FibCache
- def fib(n):
- ifn ==0:return 0
- elifn ==1:return 1
- else:returnfib(n-1) + fib(n-2)# 相当于: fib = FibCache(fib)
- # fib(10)相当于FibCache(fib)(10)
- # 装饰后的fib是FibCache的一个对象而已
- # 也就是说作为装饰器的类的构造方法要接收一个待装饰的函数,然后__call__函数的参数要和待装饰的函数的参数是一样的(除了self),这样的类就可以用来装饰函数了
Pylego: 你这个装饰器厉害,还给 fibnacci 函数加了缓存,佩服!
Jaglawz: 我不会告诉你我是看了大神的杰作然后吓吓唬吓唬你的!
来源: http://blog.csdn.net/geekleee/article/details/73555951