美文网首页魔术方法
python—闭包函数&装饰器

python—闭包函数&装饰器

作者: 小二哥很二 | 来源:发表于2019-10-06 13:47 被阅读0次

    一、闭包的定义:

    闭包函数需要满足三个条件才是闭包:

    1. 函数内部嵌套一个函数;
    2. 外层函数返回内层函数的函数名;
    3. 内层函数对外部作用域,至少有一个非全局变量的引用...
    例子NO.1
    def func(b):                          #参数b也是外部作用域的非全局变量
        a=100                             #a为外部作用域的非全局变量
        def wrapper():                    #嵌套函数
            print(b*200)                  #对外部函数的引用
            print(a+100)
            print('hello,这是内层函数的嵌套函数')
        return wrapper                    #返回内层函数
    
    c=func(2)
    print(c)
    c()
    
    运行结果:
    >>
    <function func.<locals>.wrapper at 0x0000026E640B47B8>
    400
    200
    hello,这是内层函数的嵌套函数
    >>
    =================================================
    例子NO.2
    def func(b):
        a=100
        def wrapper():
            print('----wrapper----')
            b()                  #直接调用所传入的功能函数
            print('hello,这是内层函数的嵌套函数')
        return wrapper
    
    def user_info():
        print('这个是显示个人信息的功能函数')
    
    f=func(user_info)                                          #传入的参数为一个功能函数
    f()
    
    运行结果:
    >>
    ----wrapper----
    这个是显示个人信息的功能函数
    hello,这是内层函数的嵌套函数
    <<
    

    判断是否是闭包函数:是闭包则返回cell,不是则返回None

    def outer():
        money = 0
        def inner():
            nonlocal money
            money += 100
            print(money)
        return inner
    
    res = outer()
    res()
    
    print(res.__closure__)
    print(outer().__closure__)
    ===============================================
    (<cell at 0x000001CBECE59138: int object at 0x00007FF92573EFB0>,)
    (<cell at 0x000001CBFD6008E8: int object at 0x00007FF92573E330>,)
    

    二、一般装饰器

    闭包函数就是种装饰器

    1)无参数的装饰器
    例子NO.1
    def func(b):
        a=100
        def wrapper():
            print('----wrapper----')
            b()
            print('hello,这是内层函数的嵌套函数')
        return wrapper
    
    @func           #==> user_info = func(user_info)
    def user_info():
        print('这个是显示个人信息的功能函数')
    
    user_info()     # 加了装饰器func(),这里直接调用就可以
    
    运行结果:
    >>
    ----wrapper----
    这个是显示个人信息的功能函数
    hello,这是内层函数的嵌套函数
    <<
    =================================================
    例子NO.2
    #装饰器函数
    def decorator(func):      #装饰器函数必须传参
        def wrapper():
            #再这里可以写装饰器扩展的新功能
            user=input('账号:')
            pwd=input('密码:')
            if user=='python' and pwd=='123':
                print('账号密码正确,调用原来的功能函数')
                func()
            else:
                print('账号密码错误~!')
        return wrapper
    
    @decorator    #相当于调用 user_info=decorator(user_info)
    def user_info():
        print('这个是显示个人信息的功能函数')
    
    user_info()
    
    2)内部函数有参数的装饰器
    def decorator(func):
        # 如果被装饰的函数带有参数,那么内函数必须也有对应数量的的参数
        # 因为调用原函数,等于调用内函数
        def wrapper(a,b):
            print('打印两个数相减的结果',a-b)
            func(a,b)         #调用的是add_number函数,所以要传参
        return wrapper
    
    @decorator                #==>add_number=decorator(add_number)
    def add_number(a,b):
        print('两个数相加的结果',a+b)
    
    add_number(2,3)
    
    运行结果:
    >>
    打印两个数相减的结果 -1
    两个数相加的结果 5
    <<
    

    如果被装饰的函数带有参数,那么内函数必须也有对应数量的的参数
    因为调用原函数,等于调用内函数

    3)外部函数有参数的装饰器

    如果装饰器需要有参数,那么给当前的装饰器套一个壳,用于接受装饰器的参数

    def insert_var(value):
        def outer(func):
            def inner1():
                print(f'参数{value}:妹子给了你微信')
                func()
    
            def inner2():
                print(f'参数{value}:妹子给介绍了个白富美')
                func()
            # 装饰器壳的参数,可以用于在函数内做流程控制
            if value == "白富美":
                return inner2
            else:
                return inner1
        return outer
    
    @insert_var('白富美')
    def love():
        print('淡淡人生')
    
    love()
    
    结果:
    参数白富美:妹子给介绍了个白富美
    淡淡人生
    

    三、高阶装饰器

    1)无参数的类装饰器
    Python中有一个有趣的语法,只要定义类型的时候,实现__call__函数,这个类型就成为可调用的。换句话说,我们可以
    把这个类型的对象当作函数来使用,相当于 重载了括号运算符。
    NO.1
    class logger(object):
        def __init__(self,func):
            self.func=func
        def __call__(self, *args, **kwargs):    #__call__方法接收被装饰函数,实现装饰逻辑。
            print("[INFO]:the function {func}() is running...".format(func=self.func.__name__))
            return self.func(*args,**kwargs)
    @logger
    def say(something):
        print('say{}!'.format(something))
    say('hello')
    
    NO.2
    class Person(object):
        def __init__(self, name, gender):
          self.name = name
          self.gender = gender
    
        def __call__(self, friend):
          print 'My name is %s...' % self.name
          print 'My friend is %s...' % friend
      #现在可以对 Person 实例直接调用:
    p=Person('Bob','Male')
    p('Lily')
    运行结果:
    ==>my name is Bob
    my friend is lily
    
    NO.3
    class Outer:
    
        # 魔术方法:当把该类的对象当作函数调用时,自动触发obj()
        def __call__(self, func):
            self.func = func
            return self.inner
    
        def inner(self, who):
            print('拿到了妹子的微信')
            self.func(who)
            print('看了一场午夜电影')
    
    
    @Outer()
    def love(who):
        print(f'{who}和妹子谈谈人生。。。。')
    
    love('lisa')
    
    ==>运行结果:
    拿到了妹子的微信
    lisa和妹子谈谈人生。。。。
    看了一场午夜电影
    
    2)三大内置装饰器之一:描述符 property
    NO.1 property的只读性
    class person():
        def __init__(self,name,age=18):
            self.name=name
            self.__age=18
    
        @property
        def age(self):
            return self.__age
    
    xm=person('lily')   #实例化
    print(xm.age)       #结果为18
    xm.age=22           #报错无法给年龄赋值
    print(xm.age)
    
    ==>运行结果:
        xm.age=22
    AttributeError: can't set attribute
    18
    
    NO.2 用@property装饰过的函数,会将一个函数定义成一个属性,属性的值就是该函
    数return的内容。同时,会将这个函数变成另外一个装饰器。就像后面我们使用的@age.setter和@age.deleter
    class student():
        def __init__(self,name):
            self.name=name
            self.name=None
    
        @property                     
        def age(self):                 #该方法函数为student的一个属性,属性的值就是该函数
            return self._age
    
        @age.setter                    #age函数变成另外一个装饰器
        def age(self,value):
            if not isinstance(value,int):
                raise ValueError('输入不合法,年龄必须为整数!')
            if not 0<value<100:
                raise ValueError('输入不合法,年龄必须0~100')
            self._age=value            #给装饰器property的函数赋值,将形参传递的实参赋给函数
        @age.deleter
        def age(self):
            del self._age
    xiaoming=student('小明')
    xiaoming.age=11
    print(xiaoming.age)
    del xiaoming.age
    print(xiaoming.age)
    

    四、嵌套装饰器

    def test1(f):
        def inner():
            print('成功拿到妹子的微信....3')
            f()
            print('成功约到妹子看电影....4')
        return inner
    
    def test2(f):
        def inner():
            print('我是扩展1')
            f()
            print('我是扩展2')
        return inner
    
    
    @test2
    @test1
    def love():
        print('跟妹子深入畅谈人生和理想。。。。。。5')
    
    
    love()   # 1 3 5 4 2顺序执行,先输出最外层的装饰器结果
    
    结果:
    我是扩展1
    成功拿到妹子的微信....3
    跟妹子深入畅谈人生和理想。。。。。。5
    成功约到妹子看电影....4
    我是扩展2
    

    五、函数装饰器给类装饰

    使用函数装饰器,给类进行装饰,增加新的数据和方法

    def decorate(cls):
        def func1():
            print('我是在装饰器中追加的新方法:func1')
        cls.func1 = func1   # 把刚才定义的方法赋值给类
        # 新增加的类属性
        cls.name = "我是在装饰器中追加的新属性:name"
    
        # 返回时,把追加类新成员的 类 返回
        return cls
    
    
    @decorate
    class Demo:
        def __init__(self):
            print('Demo init')
    
    
    Demo()
    Demo.func1()
    print(Demo.name)
    
    # 等于
    # res = Demo()
    # print(res.name)
    
    输出结果:
    Demo init
    我是在装饰器中追加的新方法:func1
    我是在装饰器中追加的新属性:name
    

    相关文章

      网友评论

        本文标题:python—闭包函数&装饰器

        本文链接:https://www.haomeiwen.com/subject/atrupctx.html