第四篇:装饰器

一、闭包函数

什么是闭包

内部函数对外部函数作用域里变量的引用(非全局变量),则称内部函数为闭包

        def counter():
            n=0
            def incr():
                nonlocal n
                x=n
                n+=1
                return x
            return incr

        c=counter()
        print(c())
        print(c())
        print(c())
        print(c.__closure__[0].cell_contents) #查看闭包的元素

闭包的意义与作用

#闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域
#应用领域:延迟计算(原来我们是传参,现在我们是包起来)
    from urllib.request import urlopen

    def index(url):
        def get():
            return urlopen(url).read()
        return get

    baidu=index('http://www.baidu.com')
    print(baidu().decode('utf-8'))

闭包优缺点:

1.闭包似优化了变量,原来需要类对象完成的工作,闭包也可以完成
2.由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存

二、装饰器

本质是函数 为其他函数附加其它功能
原则:
1、不修改被修饰函数的源代码
2、不修改被修饰函数的调用方式

装饰器=高阶函数+函数嵌套+闭包

1、多个装饰器装饰同一个函数
def outer_0(func):
    print('正在装饰1')
    def inner():
        print('aaa')
    return inner

def outer2(func):
    print('正在装饰2')
    def inner():
        print('bbb')
    return inner

@outer_0   
@outer2
def index():
    pass          #先执行最里面的装饰器,装的时候倒着装,执行的时候还是按照从上往下执行
正在装饰2
正在装饰1
aaa
bbb

def makeBold(fn):
    print('正在进行装饰')
    def wrapped():
        print('1')
        return "<b>" + fn() + "</b>"
    return wrapped
#定义函数:完成包裹数据
def makeItalic(fn):
    def wrapped():
        print('2')
        return "<i>" + fn() + "</i>"
    return wrapped

@makeBold   #只要python解释器执行到了这个代码,那么就会自动的进行装饰,而不是等到调用的时候才装饰的
def test1():
    return "hello world-1"

@makeItalic
def test2():
    return "hello world-2"

@makeBold    <===>test3=makeBold(makeItalic(test3))
@makeItalic  <===>test3=makeItalic(test3)
def test3():
    print('3')
    return "hello world-3"    #<b><i>hello world-3</i></b>

在调用test3之前,已经完成了装饰
test3()====>1  2  3   

2、对无参数的函数进行装饰
from time import ctime, sleep
def timefun(func):
    def wrappedfunc():
        print("%s called at %s"%(func.__name__, ctime()))
        func()
    return wrappedfunc

@timefun   <====>foo=timefun(foo)   foo()
def foo():
    print("I am foo")
上面代码理解装饰器执行行为可理解成
foo = timefun(foo)   如果函数上面有@xxx   此行就可以省略  语法糖
#foo先作为参数赋值给func后,foo接收指向timefun返回的wrappedfunc
foo()
#调用foo(),即等价调用wrappedfunc()
#内部函数wrappedfunc被引用,所以外部函数的func变量(自由变量)并没有释放
#func里保存的是原foo函数对象

3、装饰器对有参数的函数进行装饰
def func(functionName):
    print("---func---1---")
    def func_in(a, b):#如果a,b 没有定义,那么会导致test(11,22)的调用失败  
        print("---func_in---1---")
        functionName(a, b)#如果没有把a,b当做实参进行传递,那么会导致调用def test(a,b)函数失败
        print("---func_in---2---")
    print("---func---2---")
    return func_in

@func
def test(a, b):
    print("----test-a=%d,b=%d---"%(a,b))
test(11,22)

4、装饰器对有返回值的函数进行装饰
def func(functionName):
    print("---func---1---")
    def func_in():
        print("---func_in---1---")
        ret = functionName() #保存 返回来的haha
        print("---func_in---2---")
        return ret #把haha返回岛ret=test()处的调用
    print("---func---2---")
    return func_in

@func
def test():
    print("----test----")
    return "haha"

ret = test()
print("test return value is %s"%ret)

5、装饰器带有参数,在原有装饰器的基础上,设置外部变量
#带有参数的装饰器,能够起到在运行时,有不同的功能(当传入haha时执行这个方法  当传入heihei时执行另外一个方法)
def func_arg(arg):
    def func(functionName):
        def func_in():
            print("---记录日志-arg=%s--"%arg)
            if arg=="heihei":
                functionName()
                functionName()
            else:
                functionName()
        return func_in
    return func

#1. 先执行func_arg("heihei")函数,这个函数return 的结果是func这个函数的引用
#2. @func
#3. 使用@func对test进行装饰
@func_arg("heihei")
def test():
    print("--test--")

6、通用装饰器
def func(functionName):
    def func_in(*args, **kwargs):
        print("-----记录日志-----")
        ret = functionName(*args, **kwargs)
        return ret
    return func_in

@func
def test():
    print("----test----")
    return "haha"
@func
def test2():
    print("----test2---")
@func
def test3(a):
    print("-----test3--a=%d--"%a)

ret = test()
print("test return value is %s"%ret)
a = test2()
print("test2 return value is %s"%a)

装饰器什么时候进行装饰
只要python解释器执行到了@xxx这个代码,就会自动的进行装饰,而不是等到调用的时候才装饰的
当有多个装饰器包裹时,会先执行最里面的装饰器   装饰的时候倒着装

类装饰器

装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。在Python中一般callable对象都是函数,但也有例外。只要某个对象重写了 __call__() 方法,那么这个对象就是callable的
class Test():
    def __call__(self):
        print('call me!')

t = Test()
t()  # call me
__str__是print(t)时执行

类装饰器demo
class Test(object):
    def __init__(self, func):
        print("---初始化---")
        print("func name is %s"%func.__name__)
        self.__func = func   ===>指向test()函数
    def __call__(self):
        print("---装饰器中的功能---")
        self.__func() ===>保存的test()函数

@Test      <=======>Test(test)  #执行到这时自动调用__init__方法
def test():
    print("----test---")
test()   <=======>t=Test(test)   t()
1. 当用Test来装作装饰器对test函数进行装饰的时候,首先会创建Test的实例对象,并且会把test这个函数名当做参数传递到__init__方法中,即在__init__方法中的func变量指向了test函数体
2. test函数相当于指向了用Test创建出来的实例对象
3. 当在使用test()进行调用时,就相当于让这个对象(),因此会调用这个对象的__call__方法
4. 为了能够在__call__方法中调用原来test指向的函数体,所以在__init__方法中就需要一个实例属性来保存这个函数体的引用,所以才有了self.__func = func这句代码,从而在调用__call__方法中能够调用到test之前的函数体

user_list=[
    {'name':'alex','passwd':'123'},
    {'name':'linhaifeng','passwd':'123'},
    {'name':'wupeiqi','passwd':'123'},
    {'name':'yuanhao','passwd':'123'},
]
current_dic={'username':None,'login':False}

def auth(auth_type='filedb'):
    def auth_func(func):
        def wrapper(*args,**kwargs):
            print('认证类型是',auth_type)
            if auth_type == 'filedb':
                if current_dic['username'] and current_dic['login']:
                    res = func(*args, **kwargs)
                    return res
                username=input('用户名:').strip()
                passwd=input('密码:').strip()
                for user_dic in user_list:
                    if username == user_dic['name'] and passwd == user_dic['passwd']:
                        current_dic['username']=username
                        current_dic['login']=True
                        res = func(*args, **kwargs)
                        return res
                else:
                    print('用户名或者密码错误')
            elif auth_type == 'ldap':
                print('鬼才特么会玩')
                res = func(*args, **kwargs)
                return res
            else:
                print('鬼才知道你用的什么认证方式')
                res = func(*args, **kwargs)
                return res

        return wrapper
    return auth_func

@auth(auth_type='filedb') #auth_func=auth(auth_type='filedb')-->@auth_func 附加了一个auth_type  --->index=auth_func(index)
def index():
    print('欢迎来到京东主页')

@auth(auth_type='ldap')
def home(name):
    print('欢迎回家%s' %name)
#
@auth(auth_type='sssssss')
def shopping_car(name):
    print('%s的购物车里有[%s,%s,%s]' %(name,'奶茶','妹妹','娃娃'))

# print('before-->',current_dic)
# index()
# print('after--->',current_dic)
# home('产品经理')
shopping_car('产品经理')

 wraps函数

使用装饰器时,有一些细节需要被注意。例如,被装饰后的函数其实已经是另外一个函数了(函数名等函数属性会发生改变)。
添加后由于函数名和函数的doc发生了改变,对测试结果有一些影响
import functools
def note(func):
    "note function"
    @functools.wraps(func)   当未写这段代码时   test.__doc__打印的是装饰器中的文档说明
    def wrapper():
        "wrapper function"
        print('note something')
        return func()
    return wrapper
@note
def test():
    "test function"
    print('I am test')
test()
print(test.__doc__)

 

posted on 2018-05-23 11:59  liyancheng  阅读(97)  评论(0)    收藏  举报

导航