python—闭包函数&装饰器

一、闭包的定义:

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

  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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容