函数的嵌套和作用域,闭包,装饰器

函数的嵌套定义

# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/1 19:05'

def f1():
    print('in f1')
    def f2():
        print('in f2')
    f2()

f1()
# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/1 19:09'


def f1():
    def f2():
        def f3():
            print('in f3()')
        print('in f2()')
        f3()
    print('in f1()')
    f2()
f1()

nonlocal关键字的作用

在有嵌套的函数中,如果我们内部的函数使用外部的变量的时候,如果只是访问,可以正常的访问.

# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/1 19:15'
def outsite():
    var = 5
    def inside():
        print(var)
    inside()

outsite()

但是如果要修改的话,必须使用nonlocal关键字进行声明,声明这个变量是外部变量.

# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/1 19:15'
def outsite():
    var = 5
    def inside():
        # print(var) 这里会报错,nonlocal声明的变量在内部函数之前不能出现.
        nonlocal var
        var = 11
        print(var)
    inside()

outsite()

闭包的概念

首先看一个经典的闭包

# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/1 19:36'

def outer(arg):
    var = 1
    def inside():
        print(arg,var) #内部函数引用了外部变量
    return inside #外部函数返回的是内部函数

闭包的形成,必须满足三个条件
1. 必须是嵌套函数
2. 内部函数必须引用了外部变量(不是全局变量)
3. 外部函数的返回值必须是内部函数

一句话解释就是,当一个函数被当成对象返回的时候,夹带了外部变量,就形成了一个闭包.

如何理解闭包呢?

闭包的最大意义就是它带了外部函数中的变量,同一个函数可以带不同的外部变量.并且即使这个外部变量所在的外部函数不存在了,这个变量也会一直存在.它存在的方式就是在内部函数对象的closure属性,里面存放了一个元组,保存了所有的外部变量,以及它对应的值.所以内部函数携带的这个外部变量才可以持久的存储和使用.其实外部变量已经不在内存中,只是被内部函数以另外一种方式记录了下来.

如何判断一个函数是不是闭包函数

# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/1 20:02'
# 输出的__closuer__有cell元素:是闭包

def func():
    name = 'eva'
    def inner():
        print(name)
    print(inner.__closure__)
    return inner

f = func()
f()
name = 'egon'
def func():
    def inner():
        print(name)
    print(inner.__closure__)
    return inner

f = func()
f()

装饰器

装饰器函数是什么?
首先要明白两点:装饰器利用了闭包特性,还利用了函数可以作为参数和返回值使用
装饰器函数是一个嵌套函数,外层函数的返回值必须是被装饰过的函数.

简单的来说,装饰器就是一个包装函数,它本质上是一个闭包函数.一般就是传入一个函数,在不修改原来函数以及其调用方式的前提下为原来的函数增加新的功能.通常的做法是在内层的函数中调用被装饰的函数,然后再调用之前或者之后可以增加额外的代码,来增加一些额外的功能.达到扩展功能的目的.其实装饰器函数本质上是函数名和函数地址的重新绑定.被装饰的函数虽然看起来和原来的函数名字一样,但是在其内存中已经修改了绑定关系,它已经绑定到我们的装饰器函数的内层的那个闭包函数上了.

# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/1 20:28'

import time

# 装饰器函数
def deco(func):
    def wrapper():
        startTime = time.time()
        func()
        endTime = time.time()
        print('程序执行了{}秒'.format(endTime - startTime))
    return wrapper

@deco
def myfunc():
    print('hello')
    time.sleep(1)
    print('world!')

# @deco的作用就相当于是改变被装饰的函数变量的值,首先将其作为参数传递为装饰器函数,用它的返回值,赋值给被装饰的函数
# myfunc = deco(myfunc) # 相当于是myfunc = wrapper myfunc增加一个统计时间的功能
# 被装饰的函数经过装饰后,其实就变成了装饰器函数的内部函数,也就是闭包函数.

if __name__ == '__main__':
    myfunc()

装饰器的一般定义方式:

# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/1 20:46'


# 可以传递参数的装饰器函数
def timer(func):
    def inner(*args, **kwargs):
        '''被装饰函数执行之前要做的事情'''
        re = func(*args, **kwargs)
        # 被装饰函数执行之后要做的事情
        return re
    return inner

# 但是这样有一个问题,就是我们被装饰的函数的一些属性会改变.
@timer
def func1(a,b):
    print(a + b)

@timer
def func2(a):
    print(a)

print(func1.__name__,func2.__name__)

如果不想修改原来的被装饰的函数的name,doc文档结构的话,可以使用@wraps 在functiontoos包中
所以比较规范的装饰器定义如下:

# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/1 20:57'
from functools import wraps


def user_login_data(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)

    return wrapper


@user_login_data
def f1():
    print('aaa')


@user_login_data
def f2():
    print('bbb')


if __name__ == '__main__':
    print(f1.__name__)
    print(f2.__name__)

Python多个装饰器的执行顺序问题

# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/7 17:18'

def deco1(func):
    print('outer1')
    def inner1(*args,**kwargs):
        print('inner1')
        func(*args,**kwargs)
    return inner1

def deco2(func):
    print('outer2')
    def inner2(*args,**kwargs):
        print('inner2')
        func(*args,**kwargs)
    return inner2

@deco2
@deco1 # test = deco1(test) inner1   test = deco2(inner1) = inner2()
def test():
    print('test')

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

推荐阅读更多精彩内容