Python函數部分2——名稱空間與作用域,裝飾器

一、函數之名稱空間

        1.1、什麼是名稱空間

        名稱空間:存放名字的地方

        例子:S=1,1存放於內存中,那名字 S 存放在哪裏呢?名稱空間正是存放名字x與1綁定關係的地方

 

        1.2、名稱空間 (存放名字與值的綁定關係)

        1、內置名稱空間
            存放python解釋器自帶名字,比如內置的函數名:len,max,sum
            創建:隨着python解釋器啓動而創建
            銷燬:隨着python解釋器關閉而銷燬

       2、全局名稱空間

            存放文件級別的名字,比如x,f1,z
            x=1

            def f1():
            y=2

            if x == 1:
            z=3
            創建:文件開始執行時則立即創建
            銷燬:文件開始執行完畢時則銷燬

        3、局部名稱空間

            存放函數內的名字,強調:函數的參數也屬於局部的
            創建:函數執行時才臨時創建
            銷燬:函數執行完畢則立即銷燬

 

        4、名稱空間的加載順序

            內置名稱空間--->全局名稱空間--->局部名稱空間

            強調:加載的目的是爲了把名字存起來,存起來的目的是爲了取出來,那麼但凡查找一個名字一定會從三種名稱空間之一找到。

 

        5、名稱空間的查找名字順序

            局部名稱空間===>全局名稱空間===>內置名稱空間

    例子:名稱空間加載順序與名稱空間查找名字順序使用案例:

len=10
def f1():
    # len=100
    def f2():
        # len=1000
        def f3():
            # len=10000
            print(len)
        f3()
    # len=200
    f2()
len=11111111111    #這裏相當於修改了全局定義的'len=10',修改爲'len=11111111111'
f1()

len=11111111111

  

  函數的查找關係是在函數定義階段就已經固定死的,與調用位置無關

# 函數名字的查找關係是在函數定義階段就已經固定死的,與調用位置無關

x=100
def f1():
    x=10
    print(x)

def f2():
    x=111111
    f1()
f2()

>>:10

 

二、作用域

        全局範圍:內置名稱空間中的名字,全局名稱空間中的名字

            特點:全局有效,全局存活

 

        局部範圍:局部名稱空間中的名字

            特點:局部有效,臨時存活

 

        全局變量:定義在全局作用域的名字

        局部變量:定義在局部作用域的名字

 

        2.1、當全局數據類型是可變類型時,在函數內是可以修改的

l=[]             #全局類型(可變數據類型)
def foo1():
    l.append(2)  #在函數內往全局類型裏面追加數據
foo1()
print(l)         
>>:[2]              #追加完畢後,全局類型l的值已經爲[2]

        2.2、當全局數據類型是不可變類型時,在函數內不能修改

L=100             #全局類型(不可變類型)
def foo1():
    L=222         #在函數內定義L的值爲222
foo1()
print(L)
>>:100           #執行後全局類型L的值並沒有被修改

 

        2.3、global與nonlocal

        1、global用法(改全局變量的值):如果全局數據類型是不可變類型時,在函數內想要修改該變量的值的話。(global這種方式最好少用,因爲改全局變量的值後,其他代碼調用這個全局變量時也會修改)

L=100             #全局類型(不可變類型)
def foo1():
    global L      #使用global則聲明:L爲全局類型
    L=222         #在函數內定義L的值爲222
foo1()
print(L)
>>:222           #執行後全局類型L的值就被修改爲222

        2、nonlocal用法(修改當前上一層變量的值,如果沒有則報錯):nonlocal會從當前外一層開始查找,一直查找到最外層的函數,如果沒有找到則報錯

def foo1():
    x=10
    def foo2():
        nonlocal x   #使用nonlocal則聲明:x是當前函數外層的變量
        x=100
    foo2()
    print(x)    #在此處打印函數foo1內x的值
foo1()
>>:100    

 

三、函數對象

        函數對象:函數可以當做變量去處理  (把函數想想成一個變量) ,那麼:

        1、函數可以被賦值

def foo():
    print('from foo')
f = foo

print(foo)
print(f)
foo()
f()

>>:<function foo at 0x000002401456AC80>    #這是執行print(foo) 得到的
>>:<function foo at 0x000002401456AC80>    #這是執行print(f) 得到的
>>:from foo                           #這是執行foo() 得到的
>>:from foo                           #這是執行f() 得到的

 

        2、函數可以當做參數傳給一個函數 

def foo():
    print('from foo')
    print(foo)

def bar(func):  # func=foo
    print(func)
    func()

bar(foo)    #執行函數bar時傳參數爲"foo(foo爲函數),那麼func接收到的值就爲函數'foo'"

>>:<function foo at 0x000002B418DFAD08>    #這是執行print(func) 得到的
>>:from foo                                #這是執行func() 得到的,運行func()就相當於運行了foo()
>>:<function foo at 0x000002B418DFAD08>    #這是執行print(foo)得到的

 

        3、可以當做函數的返回值 

def bar():
    print('from bar')


def foo(func):      # func=bar
    return func     # return bar

f=foo(bar)

此時:f就等於函數bar,它們內存地址都是一樣的
print(f)
得到的值:<function bar at 0x000001DE62D23E18>

print(bar)
得到的值:<function bar at 0x000001DE62D23E18>

那麼執行f()就相當於執行函數bar()
f()
得到的值:from bar

bar()
得到的值:from bar


        4、可以當做容器類型元素  (這個功能常常用到)

def get():
    print('from get')

def put():
    print('from put')

def ls():
    print('from ls')

def login():
    print('from login')

main_dict={
    '1':[get,'下載'],
    '2':[put,'上傳'],
    '3':[ls,'查看'],
    '4':[login,'登錄'],
}

def run():
    while True:
        print("----------------------")
        for n in main_dict:
            print(n,main_dict[n][1])
        print('輸入 "q" 或者 "exit" 即可退出程序!')
        print("----------------------")
        choose=input('pls input num:> ').strip()
        if len(choose) == 0:continue
        if choose == 'q' or choose == 'exit':break
        if choose in main_dict:
            main_dict[choose][0]()

run()

 

四、閉包函數 == [函數嵌套+名稱空間與作用域+函數對象] 三者的綜合應用

1、什麼是閉包函數?

            1.定義在函數內的函數

            2.該函數體代碼包含對該函數外層作用域中名字的引用  ,強調:函數外層指的不是全局作用域(而是函數內部的)。

            滿足上述兩個條件,那麼該內部函數就稱之爲閉包函數。

閉包指的是一個概念,總結了:函數的作用域關係是在函數定義階段就已經固定死的,與調用位置無關。

閉包:閉的概念指的是,這個函數一定是來自於函數內的。

           包的概念指的是,這個res函數拿到的不僅僅是函數本身所具有的的數據,而是包裹了x=1這個值,那麼也就是說不管以後在哪裏調用這個res函數,包裹的x=1這個值始終不變,x的值始終爲1。

          9.5.1、閉包函數簡單例子理解

def outner():
    x=1
    def inner():
        print(x)
    return inner
res=outner()   #此時的函數res可以看出是一個全局變量,在任何地方都可以被引用--用到了函數對象的概念。[函數outner()就相當於函數inner,那麼給inner函數賦值給res的話,函數res==函數inner]
res()          #那麼在執行函數res()就相當於執行函數inner()
>>:1

def foo():             #res函數也可以被其他函數調用(函數res已經是全局變量了--用到了函數對象的概念)
    print('from foo')
    res()              
foo()
>>:from foo
>>:1

        9.5.2、閉包函數生產小例子

def sudada(url):                     #函數內傳參和在函數()傳參是一樣的
    # url='https://www.baidu.com'     #函數內傳參和在函數()傳參是一樣的
    def foo():
        import requests
        response=requests.get(url)
        if response.status_code == 200:
            print(response.text)
    return foo
baidu=sudada('https://www.baidu.com')    #變量baidu其實等於函數foo
python=sudada('https://www.python.org')  #變量python其實等於函數foo

baidu()    #執行函數baidu(),就相當於執行函數foo()
python()   #執行函數python(),就相當於執行函數foo()

 

五、裝飾器

        裝飾器常用(套用)格式:

def outter(func):
    def wrapper():
        #代碼1
        #代碼2
        #代碼n...
    return wrapper

            1 開放封閉原則
                軟件一旦上線之後就應該遵循開放封閉原則
                具體是指對修改是封閉的,但對擴展是開放的

            2、什麼是裝飾器
                裝飾就是修飾,器指的就是工具
                裝飾器本身:    可以是任意可調用的對象            --->>函數
                被裝飾的對象:也可以是任意可調用的對象         --->>函數

            3、裝飾器是用來爲被裝飾對象添加新功能的一種工具
                必須遵循:
                    1、不能修改被裝飾對象的源代碼

                    2、不能修改被裝飾對象的調用方式

        9.6.1、裝飾器例子1:

需求:查看一個index函數調用,花費了多長時間
-------------------------------------------方法1----------------------------------------
1、簡單實現需求:定義一個outer函數,在outer函數裏面調用index函數,在上一些邏輯判斷即可實現

import time
def index():
    time.sleep(1)
    print('welcome to index')

def outer(func):

        start_time = time.time()
        func()
        stop_time = time.time()
        print('run time is %s' %(stop_time - start_time))

outer(index)

得到的值如下:
  welcome to index
  run time is 1.000652551651001

-------------------------------------------方法2----------------------------------------

# 實現需求2:定義一個outer函數,在outer函數裏面再定義一個wrapper函數,通過wrapper函數來拿到index函數調用花費的時間,\
# 然後return wrapper函數,那麼調用outer函數時就會拿到一個返回值,我們可以把這個返回值定義爲 "index" 那麼使用 "index" 函數時 \
# 就相當於調用outer函數內的wrapper函數,實現的裝飾器的作用!

import time                      #導入time模塊
def index(): [被裝飾對象]          #定義函數index(),在不改變函數index()的情況下,求執行"print('welcome to index')"這段代碼的開始時間與結束時間
    time.sleep(1)
    print('welcome to index')

def outter(func): [裝飾器]         #func是outter函數的一個參數
    def wrapper():                #wrapper就是用來統計時間差,這麼一個功能
        start_time=time.time()
        func()                    #在wrapper函數內調用func()時,先在wrapper函數內找(wrapper函數內沒有),然後在outter函數內找
        stop_time=time.time()
        print('run time is %s' %(stop_time - start_time))
    return wrapper                #返回函數wrapper的內存地址

#把outter(index)[內存地址]複製給index[這裏index指的是一個變量名,無意義]
index=outter(index)               #函數outter(index)實際上就是函數wrapper的內存地址
                                  #或者說調用wrapper函數,就相當於調用index(原始)函數
index()  #執行函數index(),就相當於執行了函數wrapper()。{同時wrapper()==index()}

得到的值如下:
  welcome to index
  run time is 1.000652551651001

        9.6.2、裝飾器例子2:原始函數index裏面需要返回值時(return)

import time
def index():
    time.sleep(1)
    print('welcome to index')
    return 123    #這裏讓函數返回123

def outter(func):
    def wrapper():    #wrapper函數的功能就是:1.測試index函數運行時間,2.拿到Index函數的返回值123
        start_time=time.time()
        res=func()    #把返回值賦值給res
        stop_time=time.time()
        print('run time is %s' %(stop_time - start_time))
        return res    #在返回index函數的返回值
    return wrapper

index=outter(index)
res=index()
print(res)     

>>:welcome to index
>>:run time is 1.0006911754608154
>>:123

 

        9.6.2、裝飾器語法糖:在被裝飾對象正上方單獨一行寫@裝飾器的名字

def timmer(func):         [裝飾器名稱]
    def wrapper(*args,**kwargs):....
    return wrapper

@timmer   [裝飾器語法糖]
def index():....    [被裝飾對象]

@timmer   [裝飾器語法糖]
def home(name):....  [被裝飾對象]

 

        9.6.3、無參裝飾器

import time
def timmer(func):
    def wrapper(*args,**kwargs):    #可以接受任意值[*接受所有溢出的位置參數,**接受所有的關鍵字參數]
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print('run time is %s' %(stop_time - start_time))
        return res
    return wrapper

@timmer      # index=timmer(index)
def index():        #無參函數
    time.sleep(1)
    print('welcome to index')
    return 1234

@timmer      # home=timmer(home)
def home(name):      #有參函數,參數name
    time.sleep(2) 
    print('welcome %s to home page' %name)

index()           #此處的函數index是被裝飾器timmer裝飾後的,而不是原始的index函數
>>:welcome to index
>>:run time is 1.000145673751831

home('sudada')   #此處的函數home是被裝飾器timmer裝飾後的,而不是原始的home函數
>>:welcome egon to home page
>>:run time is 2.000617265701294

 

        9.6.4、有參裝飾器

import time
current_user={'login':False}
def auth(engine):
    def outter(func):
        def wrapper(*args,**kwargs):                   # 這是一個閉包函數
            if current_user['login']:
                return func(*args,**kwargs)            # 這調用的是被裝飾的函數[index()或者home(name)],並返回執行結果

            user=input('username>>>: ').strip()        #用戶登錄
            pwd=input('password>>>: ').strip()

            if engine == 'file':                       #判斷以哪種方式打開
                if user == 'egon' and pwd == '123':
                    current_user['login']=True
                    return func(*args,**kwargs)        # 這調用的是被裝飾的函數[index()或者home(name)],並返回執行結果
            elif engine == 'mysql':
                print('基於mysql數據的認證')
                return func(*args, **kwargs)
            elif engine == 'ldap':
                print('基於ldap的認證方式')
                return func(*args, **kwargs)
        return wrapper                                 # 返回閉包函數的執行結果
    return outter                                      # 返回閉包函數的執行結果

@auth(engine='mysql') # @outter # index=outter(index) #index=wrapper
def index():
    time.sleep(1)
    print('welcome to index')
    return 1234

@auth(engine='ldap') # @outter # home=outter(home) #home=wrapper
def home(name):
    time.sleep(2)
    print('welcome %s to home page' %name)

index()
# username>>>: szq
# password>>>: 123
# 基於mysql數據的認證

home('egon')
# username>>>: egon
# password>>>: 123
# 基於ldap的認證方式

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章