函數的嵌套和作用域,閉包,裝飾器

函數的嵌套定義

# 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('程序執(zhí)行了{}秒'.format(endTime - startTime))
    return wrapper

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

# @deco的作用就相當于是改變被裝飾的函數變量的值,首先將其作為參數傳遞為裝飾器函數,用它的返回值,賦值給被裝飾的函數
# myfunc = deco(myfunc) # 相當于是myfunc = wrapper myfunc增加一個統(tǒng)計時間的功能
# 被裝飾的函數經過裝飾后,其實就變成了裝飾器函數的內部函數,也就是閉包函數.

if __name__ == '__main__':
    myfunc()

裝飾器的一般定義方式:

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


# 可以傳遞參數的裝飾器函數
def timer(func):
    def inner(*args, **kwargs):
        '''被裝飾函數執(zhí)行之前要做的事情'''
        re = func(*args, **kwargs)
        # 被裝飾函數執(zhí)行之后要做的事情
        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包中
所以比較規(guī)范的裝飾器定義如下:

# 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多個裝飾器的執(zhí)行順序問題

# 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()
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容