Python基础知识07-装饰器
一:命名空间与作用域
1.1命名空间
Python使用叫做命名空间的东西来记录变量的轨迹。命名空间是一个 字典(dictionary) ,它的键就是变量名,它的值就是那些变量的值。
在一个 Python 程序中的任何一个地方,都存在几个可用的命名空间。
1、每个函数都有着自已的命名空间,叫做局部命名空间,它记录了函数的变量,包括函数的参数和局部定义的变量。
2、每个模块拥有它自已的命名空间,叫做全局命名空间,它记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。
3、还有就是内置命名空间,任何模块均可访问它,它存放着内置的函数和异常。
局部命名空间:
def foo(): x=1 def func(): pass
全局命名空间:
import time class ClassName: pass def foo(): pass
内键命名空间:
sum,max,min 等
1.2命名空间查找顺序
当一行代码要使用变量 x 的值时,Python 会到所有可用的名字空间去查找变量,按照如下顺序:
1、局部命名空间:特指当前函数或类的方法。如果函数定义了一个局部变量 x,或一个参数 x,Python 将使用它,然后停止搜索。
2、全局命名空间:特指当前的模块。如果模块定义了一个名为 x 的变量,函数或类,Python 将使用它然后停止搜索。
3、内置命名空间:对每个模块都是全局的。作为最后的尝试,Python 将假设 x 是内置函数或变量。
4、如果 Python 在这些命名空间找不到 x,它将放弃查找并引发一个 NameError 异常,如,NameError: name 'aa' is not defined。
嵌套函数的情况:
1、先在当前 (嵌套的或 lambda) 函数的命名空间中搜索
2、然后是在父函数的命名空间中搜索
3、接着是模块命名空间中搜索
4、最后在内置命名空间中搜索
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_ coding:utf-8 _*_ info = "Adress : " def func_father(country): city = " Beijing " def func_son(area): city= "Shanghai " #此处的city变量,覆盖了父函数的city变量 print(info + country + city + area) #调用内部函数 func_son("ChaoYang "); func_father("China ") #输出结果 #Adress : China Shanghai ChaoYang #info在全局命名空间中,country在父函数的命名空间中,city、area在自己函数的命名空间中
1.3命名空间的生命周期
不同的命名空间在不同的时刻创建,有不同的生存期。
1、内置命名空间在 Python 解释器启动时创建,会一直保留,不被删除。
2、模块的全局命名空间在模块定义被读入时创建,通常模块命名空间也会一直保存到解释器退出。
3、当函数被调用时创建一个局部命名空间,当函数返回结果 或 抛出异常时,被删除。每一个递归调用的函数都拥有自己的命名空间。
Python 的一个特别之处在于其赋值操作总是在最里层的作用域。赋值不会复制数据——只是将命名绑定到对象。删除也是如此:"del y" 只是从局部作用域的命名空间中删除命名 y 。事实上,所有引入新命名的操作都作用于局部作用域。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
i=1 def func2(): i=i+1 func2(); #错误:UnboundLocalError: local variable 'i' referenced before assignment
由于创建命名空间时,python会检查代码并填充局部命名空间。在python运行那行代码之前,就发现了对i的赋值,并把它添加到局部命名空间中。当函数执行时,python解释器认为i在局部命名空间中但没有值,所以会产生错误。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def func3(): y=123 del y print(y) func3() #错误:UnboundLocalError: local variable 'y' referenced before assignment #去掉"del y"语句后,运行正常
1.3命名空间的访问
1.3.1局部命名空间可以 locals() BIF来访问。
locals 返回一个名字/值对的 dictionary。这个 dictionary 的键是字符串形式的变量名字,dictionary 的值是变量的实际值。
示例:
def func(i, arg): x = 12345 print locals() func(1 , "first") #输出结果 #{'i': 1, 'x': 12345, 'arg': 'first'}
1.3.2全局 (模块级别)命名空间可以通过 globals() BIF来访问。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_ coding:utf-8 _*_ import copy from copy import deepcopy gstr = "global string" def func1(i, info): x = 12345 print locals() if __name__ == "__main__": func1(1 , "first") print("the current scope's global variables:") print globals() #输出结果 {'info': 'first', 'x': 12345, 'i': 1} the current scope's global variables: {'func1': <function func1 at 0x02661470>, 'gstr': 'global string', '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'D:\\Python\\test\\src\\test1.py', '__package__': None, 'deepcopy': <function deepcopy at 0x026611F0>, '__name__': '__main__', 'copy': <module 'copy' from 'C:\Python27\lib\copy.pyc'>, '__doc__': None}
总结
1、模块的名字空间不仅仅包含模块级的变量和常量,还包括所有在模块中定义的函数和类。除此以外,它还包括了任何被导入到模块中的东西。
2、我们看到,内置命名也同样被包含在一个模块中,它被称作 __builtin__。
3、回想一下 from module import 和 import module 之间的不同。
使用 import module,模块自身被导入,但是它保持着自已的名字空间,这就是为什么您需要使用模块名来访问它的函数或属性:module.function 的原因。
但是使用 from module import function,实际上是从另一个模块中将指定的函数和属性导入到您自己的名字空间,这就是为什么您可以直接访问它们却不需要引用它们所来源的模块。使用 globals 函数,您会真切地看到这一切的发生,见上面的红色输出语句。
1.3.3locals 与 globals 之间的一个重要的区别
locals 是只读的,globals 不是
#!/usr/bin/env python #_*_ coding:utf-8 _*_ def func1(i, info): x = 12345 print(locals()) locals()["x"]= 6789 print("x=",x) y=54321 func1(1 , "first") globals()["y"]= 9876 print( "y=",y) #输出结果 {'info': 'first', 'x': 12345, 'i': 1} ('x=', 12345) ('y=', 9876)
解释:
locals 实际上没有返回局部名字空间,它返回的是一个拷贝。所以对它进行改变对局部名字空间中的变量值并无影响。
globals 返回实际的全局名字空间,而不是一个拷贝。所以对 globals 所返回的 dictionary 的任何的改动都会直接影响到全局变量。
1.4作用域
作用域搜索规则:LEGB
L:局部的(local)
E:封闭的(Enclosing)
G:全局的(Global)
B:内置的(Built-in)
全局作用域与局部作用域:
#全局变量作用域,x=1为全局变量 x=1 def func1(): def func2(): def func3(): print(x) func3() func2() func1() #输出结果 #1 #局部变量作用域 x=1 def func1(): def func2(): def func3(): x=100 print(x) func3() func2() func1() #输出结果: #100
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/python # -*- coding: utf-8 -*- """ ------------------------------------------------------------------------------- Function: 【整理】Python中:self和init__的含义 + 为何要有self和__init__ https://www.crifan.com/summary_the_meaning_of_self_and___init___in_python_and_why_need_them Author: Crifan Verison: 2012-11-27 ------------------------------------------------------------------------------- """ #注:此处全局的变量名,写成name,只是为了演示而用 #实际上,好的编程风格,应该写成gName之类的名字,以表示该变量是Global的变量 name = "whole global name"; class Person: name = "class global name" def __init__(self, newPersionName): #self.name = newPersionName; #此处,没有使用self.name #而使得此处的name,实际上仍是局部变量name #虽然此处赋值了,但是后面没有被利用到,属于被浪费了的局部变量name name = newPersionName; def sayYourName(self): #此处,之所以没有像之前一样出现: #AttributeError: Person instance has no attribute 'name' #那是因为,虽然当前的实例self中,没有在__init__中初始化对应的name变量,实例self中没有对应的name变量 #但是由于实例所对应的类Person,有对应的name变量,所以也是可以正常执行代码的 #对应的,此处的self.name,实际上是Person.name print 'My name is %s'%(self.name); # -> class global name print 'name within class Person is actually the global name: %s'%(name); #-> whole global name print "only access Person's name via Person.name=%s"%(Person.name); # -> class global name def selfAndInitDemo(): persionInstance = Person("crifan"); persionInstance.sayYourName(); print "whole global name is %s"%(name); # -> whole global name ############################################################################### if __name__=="__main__": selfAndInitDemo();
二:装饰器
2.1 什么是装饰器
器即函数
装饰即修饰,意指为其他函数添加新功能
装饰器定义:本质就是函数,功能是为其他函数添加新功能
2.2 实现装饰器知识储备
装饰器=高阶函数+函数嵌套+闭包
2.3 装饰器需要遵循的原则
1.不修改被装饰函数的源代码(开放封闭原则)
2.为被装饰函数添加新功能后,不修改被修饰函数的调用方式
3装饰器是在遵守1和2的前提下,为被装饰的对象添加新功能
2.4 装饰器无参基本框架
#这就是一个实现一个装饰器最基本的架子 def timer(func): def wrapper(): func() return wrapper
2.5 装饰器有参基本框架
def timer(func): def wrapper(*args,**kwargs): func(*args,**kwargs) return wrapper
2.6 无参装饰器
#!/usr/bin/env python #_*_ coding:utf-8 _*_ import time def timer(func): #func=最原始的index def warpper(): start_time=time.time() func() end_time=time.time() print('run time is %s' % (end_time - start_time)) return warpper #无参装饰器 @timer #index=timer(index) 此时的index就是warpper def index(): time.sleep(1) print("welcome to index") index()#就是在调用warpper() #原来的index的函数只会输出welcome to index #加上装饰器后; #输出内容为: #welcome to index #run time is 1.0
2.7 有参数装饰器
#!/usr/bin/env python #_*_ coding:utf-8 _*_ import time def timer(func): #func=最原始的index def warpper(user): start_time=time.time() func(user) end_time=time.time() print('run time is %s' % (end_time - start_time)) return warpper @timer #index=timer(index) 此时的index就是warpper def home(user): time.sleep(1) print("home %s"% user) home('tom')#就是在调用warpper() #输出结果: #home tom #run time is 1.0
2.8 如果传入的参数是关键字类型
#!/usr/bin/env python #_*_ coding:utf-8 _*_ import time def timer(func): #func=最原始的index def warpper(*args,**kwargs): start_time=time.time() func(*args,**kwargs) end_time=time.time() print('run time is %s' % (end_time - start_time)) return warpper @timer #index=timer(index) 此时的index就是warpper def home(user): time.sleep(1) print("home %s"% user) home('tom')#就是在调用warpper() home(user="yyp") #输出结果: #home tom #run time is 1.0 #home yyp #run time is 1.0
2.9 如果原函数有返回值
#!/usr/bin/env python #_*_ coding:utf-8 _*_ import time def timer(func): #func=最原始的index def warpper(*args,**kwargs): start_time=time.time() res=func(*args,**kwargs) end_time=time.time() print('run time is %s' % (end_time - start_time)) return res return warpper @timer #index=timer(index) 此时的index就是warpper def home(user): time.sleep(1) return user + ' is student' res=home('tom')#就是在调用warpper() print(res) #输出结果: #run time is 1.0 #tom is student
三:闭包
闭包:本质就是一个内部函数
特点:这个内部函数必须包含对外部函数作用域(非全局作用)名字的引用
#!/usr/bin/env python #_*_ coding:utf-8 _*_ def f1(): x=1;y=2 def f2(): #f2为闭包函数 print(x) print(y) return f2 func=f1() #func() print(func.__closure__[0].cell_contents)#查看闭包函数的方法 print(func.__closure__[1].cell_contents) #输出结果: #1 #2
惰性计算:啥时候用啥时候调用
#!/usr/bin/env python #_*_ coding:utf-8 _*_ from urllib.request import urlopen def page(url): def get(): return urlopen(url).read() return get baidu=page('http://www.baidu.com') python=page('http://www.python.org') print(baidu) print(python) #输出结果(函数没有执行,已经传了一个参数): #<function page.<locals>.get at 0x0000000002DE6BF8> #<function page.<locals>.get at 0x0000000002DE6C80>
四:装饰器应用示例
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
user_list=[ {'name':'tom','passwd':'123'}, {'name':'yyp','passwd':'123'}, {'name':'sy','passwd':'123'}, {'name':'ly','passwd':'123'}, ] current_user={'username':None,'login':False} def auth_deco(func): def wrapper(*args,**kwargs): if current_user['username'] and current_user['login']: res=func(*args,**kwargs) return res username=raw_input('用户名: ').strip() passwd=raw_input('密码: ').strip() for index,user_dic in enumerate(user_list): #enumerate:python内置函数,对于一个可迭代的(iterable)/可遍历的对象(如列表、字符串),enumerate将其组成一个索引序列,利用它可以同时获得索引和值 print index, user_dic if username == user_dic['name'] and passwd == user_dic['passwd']: current_user['username']=username current_user['login']=True res=func(*args,**kwargs) return res break else: print('用户名或者密码错误,重新登录') return wrapper @auth_deco def index(): print('欢迎来到主页面') @auth_deco def home(): print('这里是你家') def shopping_car(): print('查看购物车啊亲') def order(): print('查看订单啊亲') print(user_list) index() print(user_list) home()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
user_list=[ {'name':'tom','passwd':'123'}, {'name':'yyp','passwd':'123'}, {'name':'sy','passwd':'123'}, {'name':'ly','passwd':'123'}, ] current_user={'username':None,'login':False} def auth(auth_type='file'): def auth_deco(func): def wrapper(*args,**kwargs): if auth_type == 'file': if current_user['username'] and current_user['login']: res=func(*args,**kwargs) return res username=input('用户名: ').strip() passwd=input('密码: ').strip() for index,user_dic in enumerate(user_list): if username == user_dic['name'] and passwd == user_dic['passwd']: current_user['username']=username current_user['login']=True res=func(*args,**kwargs) return res break else: print('用户名或者密码错误,重新登录') elif auth_type == 'ldap': print('巴拉巴拉小魔仙') res=func(*args,**kwargs) return res return wrapper return auth_deco #auth(auth_type='file')就是在运行一个函数,然后返回auth_deco,所以@auth(auth_type='file') #就相当于@auth_deco,只不过现在,我们的auth_deco作为一个闭包的应用,外层的包auth给它留了一个auth_type='file'参数 @auth(auth_type='ldap') def index(): print('欢迎来到主页面') @auth(auth_type='ldap') def home(): print('这里是你家') def shopping_car(): print('查看购物车啊亲') def order(): print('查看订单啊亲') # print(user_list) index() # print(user_list) home()
五:装饰器九步入门
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# -*- coding:gbk -*- '''示例1: 最简单的函数,表示调用了两次''' def myfunc(): print("myfunc() called.") myfunc() myfunc()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# -*- coding:gbk -*- '''示例2: 替换函数(装饰) 装饰函数的参数是被装饰的函数对象,返回原函数对象 装饰的实质语句: myfunc = deco(myfunc)''' def deco(func): print("before myfunc() called.") func() print(" after myfunc() called.") return func def myfunc(): print(" myfunc() called.") myfunc = deco(myfunc) myfunc() myfunc()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# -*- coding:gbk -*- '''示例3: 使用语法糖@来装饰函数,相当于“myfunc = deco(myfunc)” 但发现新函数只在第一次被调用,且原函数多调用了一次''' def deco(func): print("before myfunc() called.") func() print(" after myfunc() called.") return func @deco def myfunc(): print(" myfunc() called.") myfunc() myfunc()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# -*- coding:gbk -*- '''示例4: 使用内嵌包装函数来确保每次新函数都被调用, 内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象''' def deco(func): def _deco(): print("before myfunc() called.") func() print(" after myfunc() called.") # 不需要返回func,实际上应返回原函数的返回值 return _deco @deco def myfunc(): print(" myfunc() called.") return 'ok' myfunc() myfunc()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# -*- coding:gbk -*- '''示例5: 对带参数的函数进行装饰, 内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象''' def deco(func): def _deco(a, b): print("before myfunc() called.") ret = func(a, b) print(" after myfunc() called. result: %s" % ret) return ret return _deco @deco def myfunc(a, b): print(" myfunc(%s,%s) called." % (a, b)) return a + b myfunc(1, 2) myfunc(3, 4)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# -*- coding:gbk -*- '''示例6: 对参数数量不确定的函数进行装饰, 参数用(*args, **kwargs),自动适应变参和命名参数''' def deco(func): def _deco(*args, **kwargs): print("before %s called." % func.__name__) ret = func(*args, **kwargs) print(" after %s called. result: %s" % (func.__name__, ret)) return ret return _deco @deco def myfunc(a, b): print(" myfunc(%s,%s) called." % (a, b)) return a+b @deco def myfunc2(a, b, c): print(" myfunc2(%s,%s,%s) called." % (a, b, c)) return a+b+c myfunc(1, 2) myfunc(3, 4) myfunc2(1, 2, 3) myfunc2(3, 4, 5)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# -*- coding:gbk -*- '''示例7: 在示例4的基础上,让装饰器带参数, 和上一示例相比在外层多了一层包装。 装饰函数名实际上应更有意义些''' def deco(arg): def _deco(func): def __deco(): print("before %s called [%s]." % (func.__name__, arg)) func() print(" after %s called [%s]." % (func.__name__, arg)) return __deco return _deco @deco("mymodule") def myfunc(): print(" myfunc() called.") @deco("module2") def myfunc2(): print(" myfunc2() called.") myfunc() myfunc2()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# -*- coding:gbk -*- '''示例8: 装饰器带类参数''' class locker: def __init__(self): print("locker.__init__() should be not called.") @staticmethod def acquire(): print("locker.acquire() called.(这是静态方法)") @staticmethod def release(): print(" locker.release() called.(不需要对象实例)") def deco(cls): '''cls 必须实现acquire和release静态方法''' def _deco(func): def __deco(): print("before %s called [%s]." % (func.__name__, cls)) cls.acquire() try: return func() finally: cls.release() return __deco return _deco @deco(locker) def myfunc(): print(" myfunc() called.") myfunc() myfunc()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# -*- coding:gbk -*- '''mylocker.py: 公共类 for 示例9.py''' class mylocker: def __init__(self): print("mylocker.__init__() called.") @staticmethod def acquire(): print("mylocker.acquire() called.") @staticmethod def unlock(): print(" mylocker.unlock() called.") class lockerex(mylocker): @staticmethod def acquire(): print("lockerex.acquire() called.") @staticmethod def unlock(): print(" lockerex.unlock() called.") def lockhelper(cls): '''cls 必须实现acquire和release静态方法''' def _deco(func): def __deco(*args, **kwargs): print("before %s called." % func.__name__) cls.acquire() try: return func(*args, **kwargs) finally: cls.unlock() return __deco return _deco
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# -*- coding:gbk -*- '''示例9: 装饰器带类参数,并分拆公共类到其他py文件中 同时演示了对一个函数应用多个装饰器''' from mylocker import * class example: @lockhelper(mylocker) def myfunc(self): print(" myfunc() called.") @lockhelper(mylocker) @lockhelper(lockerex) def myfunc2(self, a, b): print(" myfunc2() called.") return a + b if __name__=="__main__": a = example() a.myfunc() print(a.myfunc()) print(a.myfunc2(1, 2)) print(a.myfunc2(3, 4))
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
'''Add decorator for each method of a class.''' import functools import threading class DecorateClass(object): def decorate(self): for name, fn in self.iter(): if callable(fn): self.operate(name, fn) class LockerDecorator(DecorateClass): def __init__(self, obj, lock=threading.RLock()): self.obj = obj self.lock = lock def iter(self): return [(name,getattr(self.obj, name)) \ for name in dir(self.obj) if not name.startswith('_')] def operate(self, name, fn): @functools.wraps(fn) def locker(*args,**kv): self.lock.acquire() try: return fn(*args, **kv) finally: self.lock.release() setattr(self.obj, name, locker) class mylocker: def __init__(self): print("mylocker.__init__() called.") def acquire(self): print("mylocker.acquire() called.") def release(self): print(" mylocker.unlock() called.") class Foo(object): def __init__(self): # Enable one or more decorators for each method: LockerDecorator(self).decorate() LockerDecorator(self, mylocker()).decorate() def interface1(self): print(" interface1() called.") def interface2(self): print(" interface2() called.") def _interface3(self): print("_interface3() called.") if __name__=="__main__": obj = Foo() obj.interface1() obj.interface2() obj.interface1() obj.interface2() obj._interface3() print(obj.interface1.__name__) ''' print(dir(obj)) print("---------------------") for item in [(name,getattr(obj, name)) for name in dir(obj)]: print(item)'''