1、函数及变量的做用html
在python程序中,函数都会建立一个新的做用域,又称为命名空间,当函数遇到变量时,Python就会到该函数的命名空间来寻找变量,由于Python一切都是对象,而在命名空间中,都是以字典形式存在着,这些变量名,函数名都是索引,而值就是,对应的变量值和函数内存地址。在python中能够用globals()查看全局变量,locals()局部变量。python
>>> global_v = '全局变量' >>> def func(): ... local_v = '局部变量' ... print(locals()) #调用locals()输出局部变量local_v >>> func() {'local_v': '局部变量'} #命名空间中都是以字典形式保存 >>> print(globals()) {.........,'global_v': '全局变量', 'func': <function foo at 0x00000092446C7F28>} #能够看到除了变量,函数名也做为索引,映射函数内存地址,是主程序命名空间的内容
能够看到内置函数globals()
返回了一个全部python能识别的变量的字典,而func 拥有本身的命名空间,里面包含了一个{'local_v': '局部变量'}元素数据库
2、变量的做用规则django
>>> name = 'lina' >>> age = 22 >>> list_1 = [1, 2, 3] >>> def fun(): ... name = 'alex' #1 尝试修改,重赋值alex 给name ... print(name) ... print(age) #2 尝试查找函数命名空间中不存在的变量age, 没找到就去外层做用域找到 ... list_1.append('local') # 4 此处修改list_1 ... list_1.pop(0) ... print(list_1) >>> fun() 'alex' 22 [2, 3, 'local'] >>> print(name) #3 查看全局变量name 是否被函数修改为功,显然没有 'lina' >>> print(list_1) [2, 3, 'local'] #4 此处修改为功
经过上一个例子,咱们能够从#1处看到,尝试给name赋值,在函数中成功了。
但是在#3处发现并无改变name的值,这是由于函数已经开辟内存复制了一份name的值到本身的命名空间中,建立一个同名的新变量name,当fun()运行结束后该内存释放,而在#3处python寻找变量时直接在本身的做用域中找到name = 'lina'。
#2处在自身的内存空间没有找到age变量,就去外层找到age= 22输出。
而在#1处就是所说的函数只能修改自身的变量,#4处对于列表、字典这种,可变对象,传过来的是内存地址,函数是复制了内存地址,而后直接去内存地址修改了,不能同变量混为一谈缓存
3、函数的形参和实参安全
对于Python来讲参数的传递是引用传递(不是值传递),形参名在函数中为局部变量。
对于不可变类型:str、number、tuple命名空间中的复制值改变不会影响外层空间的值。
可是对于可变类型如:list、dict 在函数体中的操做,可能就会改变他的值。cookie
>>> def fun(parameter): #形式参数 ... parameter = parameter*2 ... print(parameter) >>> fun(2) #实际参数 4
4、内嵌函数session
python中的内嵌函数,即在函数内部声明函数,它全部的周期和生命力仍然适用。闭包
>>> def out_fun(): ... a = '外层变量' ... def inner(): ... print(a) #1 ... inner() #2 >>> out_fun() 外层变量
5、Python中的闭包app
咱们如今把内嵌函数做为out_fun的返回值,当out_fun()被执行时,就会定义inner函数,而后返回给fun变量
>>> def out_fun(): ... a = 'out变量' ... def inner(): ... print(a) #1 ... return inner >>> fun = out_fun() >>> fun.__closure__ (<cell at 0x000000A3B57F0B28: str object at 0x000000A3B5AC3088>,)
如今来理解下这个函数,若是按照变量的做用域规则,在#1处inner首先会在本身的命名空间中去寻找变量a,没找到而后再去外层out_fun寻找。
因此当咱们执行由out_fun()返回的fun时,按照道理这个程序是会报错的。由于当out_fun()执行完毕后就会释放内存,a变量就不存在了,因此当你执行fun时,inner没法找到a变量就会报错。咱们试试看结果如何:
>>> def out_fun(): ... a = 'out变量' ... def inner(): ... print(a) ... return inner >>> fun = out_fun() >>> fun() out变量
程序并无报错,这并不矛盾,由于python支持一个名为闭包的特性,从fun.__closure__属性咱们看见了,cell at 0x000000A3B57F0B28: str object at 0x000000A3B5AC3088,
即在不是全局的定义中,定义函数inner(即嵌套函数)时,会记录下外层函数的命名空间,这个对象就保存在.__closure__属性中,去这儿就是找到外层函数的命名空间。
6、装饰器
装饰器的核心原理就是上面咱们理解到的了。装饰器是一个以函数做为参数并返回一个替换函数的可执行函数。
>>> def out_fun(fun): #1接受函数做为参数 ... def inner(a, b= 0, *args): ... print('装饰器先运行0.0') ... result = fun(a) + b #2运行传过来的被装饰函数 ... print('装饰后结果为:',result) ... return result ... return inner >>> def foo(x): #3定义foo函数 ... print('---------------\n这是被装饰函数') ... result = 2*x ... print('被装饰函数执行结果为:{}\n--------------'.format(result)) ... return 2*x >>> decorate_foo = out_fun(foo) #4将foo函数做为jout_fun参数执行out_fun >>> foo =decorate_foo #把装饰过的foo函数decorate_foo 重赋值给foo,再调用foo() >>> foo() 装饰器先运行0.0 --------------- 这是被装饰函数 被装饰执行结果为:4 --------------- 装饰后结果为: 2
如今来理解下这段程序,#1处定义了一个函数,他只接受函数做为参数,#2出运行传过来的被装饰函数,#3定义了一个函数,#4处将#3定义的foo做为参数传给out_fun(foo)获得被装饰后decorate_foo,而后再将装饰后的函数从新赋值给foo,而后当你再次运行foo函数的时候,永远都是获得被装饰后的结果了。
讲到这儿就说个实际应用列子吧!
如汽车在公路上行驶,按照某地交通规则,在国道上限速最高80迈,无下限,高速公路上最低60迈最高120迈。
咱们原始程序,经过测速传感器传来的参数计算出汽车当前速度,并返回该速度。
>>> status = 1 >>> def car_speed(angular_speed, radius = 0.35) #根据传来的角速度参数,以及半径计算出当前速度 ... current_speed = angular_speed*radius*3.6 ... return current_speed >>> >>> def slowdown(): ... pass #假设调用此函数是调用刹车、减速系统,会减慢汽车速度 >>> >>> def decorate_fun(fun): ... def inner(*args, **kwargs): ... current_speed = fun(args[0]) if len(args) = 1 else fun(args[0], radius = args[1]) ... if current_speed >110: ... sys.stdout.write('您已超速!') ... sys.stdout.flush() ... elif current_speed > 160: ... sys.stdout.write('超速50%系统已限速,请注意安全') ... sys.stdout.flush() ... slowdown() ... elif current_speed < 60: ... sys.stdout.write('该路段限速60,请注意') ... sys.stdout.flush() ... else: pass ... return current_speed ... return inner >>> >>> decorator_car_speed = decorate_fun(car_speed) >>> decorato_car_speed(120) 您已超速!
这段程序,当汽车在国道等非限速区域是,直接调用car_speed()函数就能够获得速度,而当行驶上高速公路后,就存在边界值问题,咱们可使用装饰后的decorate_car_speed()函数来处理。
7、装饰器符号@ 的应用
经过前面已经了解了装饰器原理了,这儿就简单说下@ 的应用。@ 只是python的一种语法糖而已,让程序看起更美观,简洁
>>> def decorator_foo(fun): ... def inner(*args, **kwargs): ... fun(*args, **kwargs) ... pass ... return inner >>> >>> @decorator_foo #1 >>> def foo(*args, **kwargs): #2 ... pass >>>
在#1处@decorator_foo 使用@符号+装饰器函数,在被装饰函数的上方,记住必定要正上方挨着不能空行,就等于前面所学的decorator = decorator_foo(foo)
+ foo = decorator()
这样之后你调用foo就是调用的被装饰后的foo了
8、讲一个厉害的装饰器应用
情形和需求是这样的,好比我在django view 下作用户验证(不用session),有home函数处理普通用户请求,index处理管理员请求,bbs返回论坛请求,member处理会员请求。
固然咱们若是在每个函数内都作一次验证,那代码重复就太多了,因此选择用装饰器,不失为一个好方法。但是如今们要求,根据不一样的函数,home、bbs、member都在本地数据库验证,而index作ldap验证,意思就是咱们要在一个装饰器里面,根据不一样的函数作不一样的验证。
通常的验证:
def _authentication(r): print('假设使用这个函数作本地用户认证,过了返回True,错误返回False') return #返回验证结果 def auth(fun): #装饰器函数 def wrapper(request, *args, **kwargs): if _authentication(request): #调用验证函数 result = fun(request) return result else: return '用户名或密码错了,从新登陆吧!' return wrapper @auth def index(request): pass @auth def home(request): pass @auth def bbs(request): pass @auth def member(request): pass
所有代码我就不写了,太多复杂了,就用伪代码,逻辑描述来代替了。
能够看出来,咱们这个函数能够实现用户验证功能,无论你使用cookie也好,去本地数据库取数据也罢。可是咱们上面说的需求,把index来的请求分离出来,作ldap验证,显然这样的装饰器是无法作到的。没法识别谁来的请求。
@装饰器还提供了一功能,能解决这个问题,往下看:
def _authentication(r): print('假设使用这个函数作本地用户认证,过了返回True,错误返回False') return #返回验证结果 def _ldap(r): print('ldap验证') return #返回ldap验证结果 def auth(souce_type): #这儿的souce_type参数就是@auth(v)运行时传过来的参数 def outer(fun): def wrapper(request, *args, **kwargs): if souce_type == 'local': #* 1 若是请求来源标记是'local'就本地验证 if _authentication(request): result = fun(request) return result else: return '用户名或密码错了,从新登陆吧!' elif souce_type == 'ldap': #* 1 若是请求来源标记是'ldap'就ldap验证 if _ldap(request): return fun(request) else: return '用户名或密码错了,从新登陆吧!' return wrapper return outer @auth(souce_type = 'ldap') #3 装饰 def index(request): pass @auth(souce_type = 'local') #4 def home(request): pass
人生还有意义。那必定是还在找存在的理由 转自:https://www.cnblogs.com/shiqi17/p/9331002.html
----------------------------------------------------------------------------------------------------------------------------------------------------------------
#不带参数的装饰器 @dec1 @dec2 def func(): ... #这个函数声明等价于 func = dec1(dec2(func)) #带参数的装饰器 @dec(some_args) def func(): ... #这个函数声明等价于 func = dec(some_args)(func)
不带参数的装饰器须要注意的一些细节
1. 关于装饰器函数(decorator)自己
所以一个装饰器通常对应两个函数,一个是decorator函数,用来进行一些初始化操做处理,一个是decorated_func用来实现对被装饰的函数func的额外处理。而且为了保持对func的引用,decorated_func通常做为decorator的内部函数
def decorator(func): def decorator_func() func() return decorated_func
decorator函数只在函数声明的时候被调用一次
装饰器其实是语法糖,在声明函数以后就会被调用,产生decorated_func,并把func符号的引用替换为decorated_func。以后每次调用func函数,实际调用的是decorated_func(这个很重要,装饰以后,其实每次调用的是decorated_func)。
>> def decorator(func): ... def decorated_func(): ... func(1) ... return decorated_func ... #声明时就被调用 >>> @decorator ... def func(x): ... print x ... decorator being called #使用func()函数实际上使用的是decorated_func函数 >>> func() 1 >>> func.__name__ 'decorated_func'
若是要保证返回的decorated_func的函数名与func的函数名相同,应当在decorator函数返回decorated_func以前,加入decorated_func.name = func.name, 另外functools模块提供了wraps装饰器,能够完成这一动做。
#@wraps(func)的操做至关于 #在return decorated_func以前,执行 #decorated_func.__name__ = func.__name__ #func做为装饰器参数传入, #decorated_func则做为wraps返回的函数的参数传入 >>> def decorator(func): ... @wraps(func) ... def decorated_func(): ... func(1) ... return decorated_func ... #声明时就被调用 >>> @decorator ... def func(x): ... print x ... decorator being called #使用func()函数实际上使用的是decorated_func函数 >>> func() 1 >>> func.__name__ 'func'
decorator函数局部变量的妙用
由于closure的特性(详见(1)部分闭包部分的详解),decorator声明的变量会被decorated_func.func_closure引用,因此调用了decorator方法结束以后,decorator方法的局部变量也不会被回收,所以能够用decorator方法的局部变量做为计数器,缓存等等。值得注意的是,若是要改变变量的值,该变量必定要是可变对象,所以就算是计数器,也应当用列表来实现。而且声明一次函数调用一次decorator函数,因此不一样函数的计数器之间互不冲突,例如:
#!/usr/bin/env python #filename decorator.py def decorator(func): #注意这里使用可变对象 a = [0] def decorated_func(*args,**keyargs): func(*args, **keyargs) #由于闭包是浅拷贝,若是是不可变对象,每次调用完成后符号都会被清空,致使错误 a[0] += 1 print "%s have bing called %d times" % (func.__name__, a[0]) return decorated_func @decorator def func(x): print x @decorator def theOtherFunc(x): print x
下面咱们开始写代码:
#coding=UTF-8 #!/usr/bin/env python #filename decorator.py import time from functools import wraps def decorator(func): "cache for function result, which is immutable with fixed arguments" print "initial cache for %s" % func.__name__ cache = {} @wraps(func) def decorated_func(*args,**kwargs): # 函数的名称做为key key = func.__name__ result = None #判断是否存在缓存 if key in cache.keys(): (result, updateTime) = cache[key] #过时时间固定为10秒 if time.time() -updateTime < 10: print "limit call 10s", key result = updateTime else : print "cache expired !!! can call " result = None else: print "no cache for ", key #若是过时,或则没有缓存调用方法 if result is None: result = func(*args, **kwargs) cache[key] = (result, time.time()) return result return decorated_func @decorator def func(x): print 'call func'
随便测试了下,基本没有问题。
>>> from decorator import func initial cache for func >>> func(1) no cache for func call func >>> func(1) limit call 10s func 1488082913.239092 >>> func(1) cache expired !!! can call call func >>> func(1) limit call 10s func 1488082923.298204 >>> func(1) cache expired !!! can call call func >>> func(1) limit call 10s func 1488082935.165979 >>> func(1) limit call 10s func 1488082935.165979
博客地址:http://www.cnblogs.com/elyw/p/python_function_decorator_and_lambda.html