溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Python名稱空間及作用域怎么用

發(fā)布時間:2022-01-10 14:20:20 來源:億速云 閱讀:100 作者:iii 欄目:開發(fā)技術(shù)

今天小編給大家分享一下Python名稱空間及作用域怎么用的相關(guān)知識點,內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

    名稱空間

    什么是名稱空間

    在Python中名稱空間是用存儲對象和名字綁定關(guān)系的地方,那么問題來了,什么是對象,什么是名字,什么是綁定關(guān)系?

    1)在目前,我們對于對象的認(rèn)知可以暫時只停留在人云亦云的“Python中一切都是對象”基礎(chǔ)上,函數(shù)是對象、類是對象、變量,模塊、所有一切都是對象,有這樣的認(rèn)知就可以了,后續(xù)有機(jī)會將繼續(xù)補充;

    2)名字,很簡單,每一次我們對模塊、變量、函數(shù)、類的定義都需要取名字,而這些名字都會放在名稱空間之中;

    3)Python對于名字和數(shù)據(jù)之間給出了綁定關(guān)系,舉個例子,當(dāng)我們在定義a = 6時,Python就自動將變量a這個名字與6這個對象給出了綁定關(guān)系,我們可以使用del語句將綁定關(guān)系解除。

    明白了名稱空間是用于存儲對象和名字綁定關(guān)系的地方,那么接下來就可以細(xì)致了解一下名稱空間可以分為哪幾類了:

    1)內(nèi)置名稱空間 —— 用于存放各種內(nèi)置函數(shù)(built-in functions)、內(nèi)置模塊(built-in modules),例如abs()就是內(nèi)置函數(shù),內(nèi)置名稱空間可以在Python任何一處使用;

    2)全局名稱空間 —— 全局名稱空間中的名字可以在同一個模塊中任意處使用;

    3)局部名稱空間 —— 局部名稱空間中的名字僅僅只能夠在函數(shù)內(nèi)部使用。

    名稱空間的意義

    名稱空間最大的作用就是防止名字重復(fù)造成的引用不當(dāng),我們可以在全局名稱空間中定義一個a = 6同時也可以在局部名稱空間中定義一個a = 7,這兩者之間是不會產(chǎn)生任何沖突的,這就是名稱空間最大的作用,防止名字重復(fù)造成的引用不當(dāng)。

    名稱空間的查找順序

    知道了名稱空間的意義,那么肯定會有讀者意識到,我在全局定義一個a = 6,在局部定義一個a = 7,那么接下來調(diào)用a這個名字的時候,Python究竟會從哪個空間開始尋找a所對應(yīng)的對象呢?

    我只能說,這位讀者你很上道,我們將以實例解答這個問題;

    a = 6              # 在全局名稱空間中定義一個a
    b = 8            # 在全局名稱空間中定義一個b,為了測驗調(diào)用函數(shù)時能否找到全局中的b
    def test():
        a = 7        # 在局部名稱空間中定義一個a
        return a,b
    print(test())
    print(a)

    (7,8)
    6

    從以上我們的測驗中,調(diào)用函數(shù)test時輸出的a將會是7,而當(dāng)直接使用print(a)時輸出的a將會是6。

    所以我們可以大膽的下結(jié)論:

    1)當(dāng)調(diào)用函數(shù)的時候,函數(shù)尋找名字的順序?qū)?局部名稱空間—>全局名稱空間—>內(nèi)置名稱空間;

    2)當(dāng)沒有調(diào)用函數(shù),直接使用名字的時候查找順序就是 全局名稱空間 —>內(nèi)置名稱空間;

    3)只要在某個名稱空間(局部也好、全局也罷)中找到了對應(yīng)的名字,就停止尋找;

    4)在不同名稱空間中定義相同名字是可行的,后續(xù)定義的并不會將原先覆蓋掉。

    局部名稱空間詳解

    在局部名稱空間中有一個非常神奇的事情,因為函數(shù)是可以相互嵌套的,在一個函數(shù)中嵌套另外一個函數(shù)是很正常的現(xiàn)象:

    def test_1():           # 定義一個函數(shù)
        def test_2():       # 在test_1中定義一個嵌套函數(shù)
            print('球球好心人給個贊吧')
    # 這是最簡單的函數(shù)嵌套,
    # 但也是最不規(guī)范的函數(shù)嵌套,
    # 因為如果不改進(jìn)的話,則無法使用嵌套的test_2函數(shù)

    以上就是最簡單形式的函數(shù)嵌套,那么問題接踵而至,上文中說過局部命中空間是在函數(shù)中產(chǎn)生的,那么如果我在一個函數(shù)中定義一個嵌套函數(shù),是不是意味著我在局部名稱空間中創(chuàng)建了一個局部名稱空間?

    對頭!

    但是在術(shù)語上我們會稱test_2為最內(nèi)部名稱空間,而test_1則是被我們稱為附屬函數(shù)名稱空間;

    我們可以如此反復(fù)俄羅斯套娃:

    def test_1():           # 定義一個函數(shù)
        def test_2():       # 在test_1中定義一個嵌套函數(shù)
            def test_3():   # 在內(nèi)嵌函數(shù)test_2中再定義一個嵌套函數(shù)
                # 省略一萬層....
                print('球球好心人給個贊吧')
            print('球球好心人給個贊吧')
    嵌套函數(shù)中的查找順序

    在前文中已經(jīng)介紹過了關(guān)于嵌套函數(shù)所產(chǎn)生的附屬函數(shù)名稱空間、內(nèi)部名稱空間,那么如果在附屬函數(shù)名稱空間和內(nèi)部名稱空間都定義一個相同名字,那么查找順序是如何呢?

    b = 10                    # 在全局定義一個b
    def test_1():           # 定義一個函數(shù)
        def test_2():       # 在test_1中定義一個嵌套函數(shù)
            a = 6           # 在內(nèi)部名稱空間中定義一個a
            return a,b
        a = 7                # 在附屬名稱空間中定義一個a
        b = 8               # 在附屬名稱空間中定義一個b
    print(test_1())            # 調(diào)用函數(shù)

    如果真的如上文中這樣寫的話,那么將不會輸出任何結(jié)果哦,因為我們只調(diào)用了test_1,而作為嵌套的內(nèi)部函數(shù)test_2沒有被使用到,想要使用嵌套函數(shù)的話,就只能通過將嵌套函數(shù)作為返回值,返回出去

    所以將代碼修改一下

    b = 10                    # 在全局定義一個b
    def test_1():           # 定義一個函數(shù)
        def test_2():       # 在test_1中定義一個嵌套函數(shù)
            a = 6           # 在內(nèi)部名稱空間中定義一個a
            return a,b
        a = 7                # 在附屬名稱空間中定義一個a
        b = 8               # 在附屬名稱空間中定義一個b
        return test_2
    print(test_1()())        # 調(diào)用函數(shù)

    10
    (6,8)

    按照修改后,我們所得到的結(jié)果將會是6,當(dāng)我們調(diào)用嵌套函數(shù)的時候,嵌套函數(shù)會從自身的局部空間中開始尋找是否有該名稱

    就像調(diào)用嵌套函數(shù)test_2一般,它從自己的局部名稱空間開始尋找,找到了a = 6后就停止尋找

    所以我們又可以下結(jié)論了:

    1)當(dāng)調(diào)用嵌套函數(shù)的時候,它的查找順序是 內(nèi)部名稱空間—>附屬函數(shù)名稱空間—>全局名稱空間—>內(nèi)置名稱空間;

    2)找到對應(yīng)的名字后就會停止尋找。

    關(guān)于嵌套函數(shù)的使用
    b = 10                    
    def test_1():           
        def test_2():       
            a = 6           
            return a,b
        a = 7                
        b = 8               
        return test_2
    print(test_1()())                # 仔細(xì)看一下調(diào)用函數(shù)的過程

    為什么調(diào)用函數(shù)過程中需要寫兩個括號test_1()(),而不是直接test_1()呢?

    我們仔細(xì)看一下test_1函數(shù)的返回值,test_1的返回值是一個函數(shù)對象test_2,所以我們?nèi)绻{(diào)用函數(shù)的話只寫一個括號將會得到一個函數(shù)對象,也就是test_2

    來實例示范一下;

    b = 10                    
    def test_1():           
        def test_2():       
            a = 6           
            return a,b
        a = 7                
        b = 8               
        return test_2
    print(test_1(), type(test_1()))        # 打印輸出一下結(jié)果

    <function test_1..test_2 at 0x000001A8E9981F30> <class &lsquo;function&rsquo;>
    以上就是打印輸出的結(jié)果,代表了函數(shù)對象

    可能有讀者想要唱反調(diào)了,我就是想直接寫一個test_1()就能夠直接得到想要的結(jié)果該怎么辦呢?

    我只能說,這位看官你很有成為天才的潛力,因為懶才是人類進(jìn)步的基石,這個需求可以實現(xiàn),但是我們要這樣改代碼:

    b = 10                    
    def test_1():           
        def test_2():       
            a = 6           
            return a,b
        a = 7                
        b = 8               
        return test_2()
    print(test_1())                # 調(diào)用函數(shù)

    (6,8)

    我們的確得到了想要的結(jié)果,仔細(xì)想一下為什么呢?

    還是因為返回值,函數(shù)test_1的返回值是test_2(),也就是說返回的結(jié)果是函數(shù)test_2運行后的結(jié)果,又開始俄羅斯套娃了,我拿到的返回值是另外一個函數(shù)的返回值?。?/p>

    我只能說沒錯,是這樣的。

    但是用這個方法需要注意一點,那就是內(nèi)嵌函數(shù)必須是無參數(shù)的!

    b = 10                    
    def test_1():           
        def test_2(c):           # 隨便定義一個參數(shù)
            a = 6           
            return a,b
        a = 7                
        b = 8               
        return test_2()
    print(test_1())                # 調(diào)用函數(shù)

    TypeError: test_1..test_2() missing 1 required positional argument: &lsquo;c&rsquo;
    這將會報錯,給出的錯誤是函數(shù)test_2()缺失一個名為&rsquo;c&rsquo;的位置參數(shù)
    所以想要使用這種方法還是需要注意下的,
    但是換另外一種思路,如果內(nèi)嵌函數(shù)需要參數(shù),那么我返回的時候先把參數(shù)定義不行么?
    這種方法的確是可行的,
    但是如果這樣的話那不如直接使用默認(rèn)參數(shù),在定義的時候直接將參數(shù)c定義好

    以上就是關(guān)于俄羅斯套娃的名稱空間的講解,接下來我們要介紹一下作用域了,如果能夠?qū)⒚Q空間中的知識點李姐,那么作用域也不過爾爾。

    作用域

    什么是作用域

    作用域是根據(jù)名稱空間所產(chǎn)生的,意思就是名字的作用范圍;

    在上文之中我們其實已經(jīng)或多或少涉及到了作用域了。

    b = 10                            # 這個全局變量的作用域就是該模塊中的全部范圍        
    def test_1():           
        def test_2():       
            a = 6           
            return a,b                # 正因為全局變量的作用于是全部范圍,才能夠返回b
        a = 7                
        b = 8               
        return test_2
    print(test_1()())

    或多或少讀者對于這個作用域已經(jīng)有些許了解,

    我直接將結(jié)論擺出:

    內(nèi)置名稱空間 &mdash;&mdash; 其作用域是Python中的所有模塊,能夠在所有的模塊中使用;
    全局名稱空間 &mdash;&mdash; 其作用域是該模塊的所有范圍,能夠在模塊內(nèi)隨意使用;
    局部名稱空間 &mdash;&mdash; 其作用域僅僅在于該函數(shù)內(nèi)部,只能夠在函數(shù)內(nèi)部使用。

    可能正是因為作用域的不同,所以查找順序也會不同,作用域越大的名稱空間反而查找的優(yōu)先級越低,

    正如上文中的,即使全局和局部中都有a這個名字,調(diào)用函數(shù)的時候也會先從局部開始。

    global語句

    不同的名稱空間可以定義相同的名字,這樣不會有任何沖突,可這也意味著,當(dāng)我們在局部名稱空間的時候是無法修改全局中的名字綁定關(guān)系,于是Python提供了一個方法去解決這個問題:

    a = 10               # 定義一個全局語句
    
    def test():
        global a        # 使用global語句,聲明我是用的名字a全局名稱空間中的那個a
        a = 5
        return a
    
    print(a)            # 先打印輸出一下沒有調(diào)用函數(shù)前的a是什么
    print(test())        # 輸出一下函數(shù)中的結(jié)果
    print(a)            # 看一下全局中的a是否發(fā)生了改變

    10
    5
    5

    所以我們可以知道,使用global語句后,我們使用的名字都將會是全局名稱空間的

    nonlocal語句

    既然在局部可以修改全局名稱空間中的名字綁定關(guān)系,那么在內(nèi)部名稱空間是否可以修改附屬函數(shù)名稱空間中的綁定關(guān)系呢?

    答案顯然是可以的,但是需要使用到nonlocal語句。

    def test_1():
        def test_2():				# 在test_1里定義一個嵌套函數(shù)test_2
            a = 15
    
            def test_3():			# 俄羅斯套娃一波
                nonlocal a			# 使用nonlocal,聲明接下來使用的a是附屬名稱空間的a,所以究竟是test_2中的還是test_1中的?
                a = 5
                print('調(diào)用test_3對a這個名字的綁定關(guān)系進(jìn)行更改')
    
            test_3()
            print(f'輸出附屬函數(shù)名稱空間中的a:{a}')
    
        a = 10
        test_2()
        print(f'輸出最外部函數(shù)的a值:{a}')
    
    
    test_1()

    調(diào)用test_3對a這個名字的綁定關(guān)系進(jìn)行更改
    輸出附屬函數(shù)名稱空間中的a:5
    輸出最外部函數(shù)的a值:10

    由此我們可以知道,在調(diào)用nonlocal聲明使用的a是函數(shù)test_2中的,而不是test_1中的

    因此我們可以得出的結(jié)論是:

    1)在使用nonlocal語句的過程中,僅僅只會向上尋找到對應(yīng)名字修改一次

    題目題目

    還是老規(guī)矩,寫一個題目對上述內(nèi)容進(jìn)行測試

    def discount(price,rate):
        final_price = price * rate
        old_price = 6
        print('old_price的值',old_price)
        return final_price
    
    old_price = float(input('請輸入價格'))
    rate = float(input('請輸入折扣率'))
    print(discount(old_price,rate))
    print('old_price的值',old_price)

    在上述代碼中,假設(shè)我輸入的old_price是100,rate是0.6
    那么請問兩個問題

    print(discount(old_price,rate))中會輸出的值是多少?
    print('old_price的值',old_price)輸出的值是多少?

    小結(jié)

    1. 命名空間是一個名字(變量)和對象的映射表。

    2. 作用域是指命名空間的作用范圍,或者說管轄區(qū)域。

    3. 變量的查找遵循 LEGB 原則,先從基層(最內(nèi)層函數(shù)找),然后到市委(外層函數(shù))&hellip;,再到省委(模塊命名空間),最后到中央(builtin 命名空間)。

    4. 各個命名空間相互獨立,創(chuàng)建時間和生命周期各不相同。

    5. global 用于在函數(shù)內(nèi)創(chuàng)建和修改全局變量。

    6. nonlocal 用于在內(nèi)層函數(shù)修改外層函數(shù)局部變量。

    7. 沒有聲明 global 和 nonlocal,嘗試修改全局變量或外層函數(shù)局部變量,實際上只會在函數(shù)或者內(nèi)層函數(shù)創(chuàng)建一個新的局部變量,同名的全局變量或者外層函數(shù)局部變量不會受影響。

    能夠看到這里的都是一條漢子!

    以上就是“Python名稱空間及作用域怎么用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學(xué)習(xí)更多的知識,請關(guān)注億速云行業(yè)資訊頻道。

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI