初學者教程之命名空間,範圍解析及LEDB規則

來源:互聯網
上載者:User

標籤:方向   這不   檢查   層級   cafe   不同   解決方案   unbound   調用   

2014年5月12日

Sebastian Raschka編寫

    這是一篇關於採用LEGB規則實現Python變數命名空間及範圍解析的簡短教程。下面章節將會提供簡短的可以說明問題的範例程式碼塊來簡要闡述問題。您可以簡單的從頭至尾閱讀本教程,但我鼓勵您去執行這些程式碼片段。你可以複製粘貼這些程式碼片段,但是為了方便您也可以下載IPython筆記。

章節

? 章節

? 目標

? 命名空間和範圍介紹

o 命名空間

o 範圍

o 提示:

o 通過LEGB規則解析變數名的範圍

?1. LG-本地和全域範圍

o 原因:

o 原因:

?2. LEG – 局部、封閉和全域範圍

o 原因:

?3. LEGB – 局部、封閉、全域、內建

o 原因:

? 自由練習

? 結論

o 經驗法則

o 解決方案

o 警告:對於迴圈變數“leaking”加入全域命名空間

目標

  ?命名空間和範圍:Python是從哪裡尋找變數名?

  ?我們可以在同一時間定義或重用多個對象的變數名嗎?

  ?Python是通過哪種方式為變數名搜尋不同的命名空間的呢?

命名空間和範圍介紹

命名空間

    大致來說,命名空間只是將名稱映射到對象的容器。正如你可能已經聽過,Python中所有的字串、列表、函數、類等等都是對象。如此,“對象與名稱”的映射關係允許我們使用已經為這個對象分配的名稱來訪問這個對象。例如,如果我們做一個簡單的字串分配,可以通過:a_string = “Hello string”, 我們建立了一個關於“Hello string”的對象,從此以後我們就可以通過它的變數名a_string來訪問它。

    我們可以想象一個命名空間為Python字典結構,字典的鍵在哪裡代表它的名字以及字典的值在哪裡代表對象本身(這也是目前在Python中命名空間該如何?),例如,

a_namespace = {‘name_a‘:object_1, ‘name_b‘:object_2, ...}

    現在,棘手有部分是在Python中我們有多個獨立的命名空間,並且名稱可以被不同的命名空間重複使用(只有對象是唯一的,例如,

a_namespace = {‘name_a‘:object_1, ‘name_b‘:object_2, ...

b_namespace = {‘name_a‘:object_3, ‘name_b‘:object_4, ...}

    例如,當我們調用一個迴圈或定義一個函數時,它將建立自己的命名空間。命名空間也有不同的層次(所謂的“範圍”),我們將在下一節中更詳細地討論。

範圍

    在上述章節中,我們已經瞭解到命名空間可以獨立存在,彼此和他們的結構在一定的層次,這給我們帶來了“範圍”。Python 中的“範圍”在我們為“命名物件”映射搜尋命名空間時定義了“層級”。

    例如,讓我們思考一下下面的這段代碼:

    在這裡,我們僅僅只對變數名i定義了兩次,一次是是在foo函數中。

       ?foo_namespace = {‘i‘:object_3, ...}

       ?global_namespace = {‘i‘:object_1, ‘name_b‘:object_2, ...}

     所以,如果我們想要列印變數i的值,Python是如何知道應該尋找哪個命名空間呢?這個地方就需要Python的LEGB規則來實現了,我們將在下個章節討論。

提示:

    如果我們想要列印出字典的全域變數和局部變數的映射,我們可以使用global()和local()函數。

通過LEGB規則解析變數名範圍

    我們已經看到,多個命名空間可以彼此獨立存在,它們可以在不同的層次水平包含相同的變數名。“範圍”定義在哪個層次,Python為其相關對象搜尋了一個特定的“變數名”。現在,下一個問題是:Python是採用什麼方式在它找到對象名稱的映射之前搜尋命名空間的不同層級呢?

     答案是:Python使用LEGB規則,即

     局部->封閉->全域->內建

    箭頭應該指向命名空間階層搜尋順序的方向。

      ?局部可以在函數內部或類方法中,例如。

       ?封閉可以是它的enclosing方法,例如,一個方法被包含在另一個方法中。

       ?全域是指執行指令碼的最進階別,以及

       ?內建是Python自己保留的特殊名稱。

    所以,如果一個特定的名稱:對象映射不能在本地命名空間中找到,下一步封閉範圍的命名空間將會被搜尋。如果在封閉範圍的搜尋也是不成功的,Python將會到全域命名空間去搜尋,最終,它將搜尋內建命名空間(附註:如果名稱在任何的命名空間都找不到,系統將報告NameError)。

註:

    命名空間還可以嵌套,例如如果我們匯入的模組,或者如果我們定義新類。在這種情況下,我們必須使用首碼來訪問這些嵌套的命名空間。讓我在下面的代碼塊中說明這個概念:

   (這也是為什麼我們在通過“from a module import *”時必須注意,因為在載入變數名到全域命名空間時很可能會覆蓋已經存在的變數名。)

1. LG – 局部和全域範圍

例 1.1

作為熱身練習,讓我們先忘記在LEGB規則中封閉(E)和內建(B)的範圍,來看一看LG----局部和全域範圍。

    下面的代碼將列印什麼呢?

原因:

    我們首先調用a_func(),這應該是列印a_var值。根據LEGB規則,這個方法將會首先在局部範圍(L)查看是否a_var是被定義的。因為a_func()沒有定義自己a_var,它會向上一級全域範圍(G)尋找,直到a_var之前定義的範圍。

例 1.2

現在,讓我們在全域和局部範圍定義變數a_var.

你能猜到下面的代碼將會產生什嗎?

原因:

    當我們調用a_func()時,首先它會在局部範圍(L)中尋找a_var,因為a_var已經在局部範圍a_func中定義,它的賦值local varible就會被列印。注意這不會影響在不同範圍的全域變數。

    然後,它也在可能會修改全域的,例如,如果我們在重新賦值時使用全域關鍵字。下面的例子將會說明:

    但是我們必須小心這個順序:如果我們沒有明確的告訴Python我們想要使用全域範圍來嘗試修改變數值就很容易出現UnboundLocalError錯誤。(記住,賦值操作先執行的是右邊的操作):

2. LEG – 局部, 封閉和全域範圍

    現在,讓我們來介紹一下封閉範圍(E)的概念。按照“局部->封閉->全域”的順序,你能猜出下面的代碼將列印什麼嗎?

例 2.1

原因:

    讓我們快速概括我們在做什麼: 我們調用outer(), 其變數a_var在局部被定義(a_var在全域存在)。接下來, outer()函數調用同樣定義了名字為a_var的變數的inner()函數。Inner()中的print()函數在它的範圍層級結構之前先在局部範圍中尋找(L->E),因此它列印了在局部範圍內分配的值。

類似於我們已經在前面章節中看到的global關鍵字的概念,我們可以使用包含在內建函數的關鍵字nonlocal來明確地訪問外部(封閉)範圍的變數,以修改它的變數值。

    注意nonlocal關鍵字已經添加到了Python 3.x, 在Python 2.x中還沒有實現。


3. LEGB – 局部,封閉,全域,內建

     總結LEGB規則,讓我們進入內建範圍。在這裡,我們定義了我們“own”的長度函數,這恰好為內建的len()函數具有相同的名稱。如果我們執行了下面的代碼,結果是什嗎?

例 3

原因:

    因為相同的名稱可用於映射不同的對象,只要這個名字在不同的名字空間並且沒有重用的名字來定義自己的函數len長度的問題(這隻是用於示範,不推薦)。當我們回顧Python的L - >E> G > B層次,函數a_func()發現在它嘗試搜尋內建(B)命名空間之前len()已經在全域範圍(G)。

自由練習

    現在,在我們經過幾次聯絡之後,讓我們快速檢查我們掌握到哪裡?因此,多一次的練習:下面的代碼將列印出什麼呢?


結論

    我希望通過這個簡短的教程可以有助於理解Python利用LEGB規則來進行範圍解析順序的基本的概念。我想鼓勵你(作一次小的自由練習)明天再去看看程式碼片段,再檢查一下你是否能正確的預測出它們的結果。

經驗法則

    在實踐中,在函數範圍修改全域變數通常是一個壞想法,因為它往往會引起一些混亂和奇怪的錯誤,並且很難調試。

如果你想通過函數修改一個全域變數,建議你通過參數傳入該全域變數並將傳回值重新指定給全域變數。

例如:


解決方案

    為上防止你無意的破壞,我已經用二進位格式編寫瞭解決方案。 為了顯示這些字元表示,你只需要執行下面的幾行代碼:


警告:對於迴圈變數“leaking”加入全域命名空間

    和其他的變成語言相反,for-loops將使用他們存在的範圍並留下他們已經定義的迴圈變數。

這也使用於如果我們明確在全域命名空間之前定義for-loop變數!在這種情況下,它會重新綁定存在的變數。

    無論如何,在Python 3.x中,我們可以使用閉包來防止迴圈變數將其分割到全域命名空間中。

    為什麼我提到“Python 3.x”?  因為當問題發生時,相同的代碼在Python 2.x的執行結果是這樣的:

英文原文:http://sebastianraschka.com/Articles/2014_python_scope_and_namespaces.html
譯者:linkxu1989

初學者教程之命名空間,範圍解析及LEDB規則

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.