簡體   English   中英

為什么 Python 的非本地關鍵字不像全局 scope?

[英]Why doesn't Python's nonlocal keyword like the global scope?

在 Python 3.3.1 中,這有效:

i = 76

def A():
    global i
    i += 10

print(i) # 76
A()
print(i) # 86

這也有效:

def enclosing_function():
    i = 76
    def A():
        nonlocal i
        i += 10

    print(i) # 76
    A()
    print(i) # 86

enclosing_function()

但這不起作用:

i = 76
def A():
    nonlocal i # "SyntaxError: no binding for nonlocal 'i' found"
    i += 10

print(i)
A()
print(i)

nonlocal關鍵字狀態的文檔(已添加重點):

nonlocal 語句導致列出的標識符引用最近的封閉 scope中先前綁定的變量。

在第三個示例中,“最近的封閉范圍”恰好是全局 scope。 那么為什么它不起作用呢?

請閱讀本文

我確實注意到文檔繼續到 state(已添加重點):

[ nonlocal ] 語句允許封裝代碼重新綁定本地 scope除了全局(模塊) scope 之外的變量

但是,嚴格來說,這並不意味着我在第三個示例中所做的事情不應該起作用。

名稱的搜索順序是LEGB,即Local,Enclosing,Global,Builtin。 因此全局范圍不是封閉范圍。

編輯

來自文檔

非本地語句使列出的標識符引用最近的封閉范圍中的先前綁定的變量。 這很重要,因為綁定的默認行為是首先搜索本地名稱空間。 除了全局(模塊)范圍之外,該語句還允許封裝代碼重新綁定局部范圍之外的變量。

答案是全球范圍並沒有包含任何內容 - 它對所有事物都是全球性的。 在這種情況下使用global關鍵字。

為什么模塊的范圍被認為是全局的而不是封閉的? 它仍然不是其他模塊的全局(好吧,除非你from module import * ),是嗎?

如果你在module的命名空間中加入一些名稱; 它在任何使用module模塊中都可見,即它對於整個Python進程是全局的。

通常,您的應用程序應使用盡可能少的可變全局變量。 看看為什么全局變壞了?

  • 非局域
  • 無訪問控制或約束檢查
  • 隱含耦合
  • 並發問題
  • 命名空間污染
  • 測試和限制

因此,如果nonlocal允許意外創建全局變量,那將是很糟糕的。 如果要修改全局變量; 你可以直接使用global關鍵字。

  • global是最具破壞性的:可能會影響程序中任何位置的模塊的所有使用
  • nonlocal的破壞性較小:受outer()函數范圍的限制(在編譯時檢查綁定)
  • no聲明(局部變量)是破壞性最小的選項:受inner()函數范圍的限制

您可以在PEP中閱讀nonlocal背后的歷史和動機:3104訪問外部范圍內的名稱

這取決於邊界情況:

nonlocals帶有一些我們需要注意的敏感區域。 首先,與全局語句不同,當評估非本地語時, 非本地名稱實際上必須先在封閉的def范圍內分配,否則您將收到錯誤 - 您無法通過在封閉范圍內重新分配它們來動態創建它們。 實際上,在調用任一函數或嵌套函數之前,會在函數定義時檢查它們

>>>def tester(start):
      def nested(label):
         nonlocal state   #nonlocals must already exist in enclosing def!
         state = 0
         print(label, state)
      return nested
SyntaxError: no binding for nonlocal 'state' found

>>>def tester(start):
      def nested(label):
          global state   #Globals dont have to exits yet when declared
          state = 0      #This creates the name in the module now
          print(label, state)
      return nested

>>> F = tester(0)
>>> F('abc')
abc 0
>>> state
0

其次, nonlocal將范圍查找限制為僅包含defs; 在封閉模塊的全局范圍內或在所有def之外的內置范圍中都不會查找nonlocals,即使它們已經存在:

例如:-

>>>spam = 99
>>>def tester():
      def nested():
         nonlocal spam  #Must be in a def, not the module!
         print('current=', spam)
         spam += 1
      return nested
SyntaxError: no binding for nonlocal 'spam' found

一旦你意識到python通常不會知道創建一個全新名稱的封閉范圍,這些限制是有意義的。在之前的列表中, 垃圾郵件是應該在測試人員中分配還是在外面的模塊中分配? 因為這是不明確的,Python必須在函數創建時解析非局部,而不是函數調用時間。

歷史原因

在 2.x 中, nonlocal還不存在。 沒有必要修改封閉的非全局范圍; 全局 scope 被視為一個特例。 畢竟,“全局變量”的概念比詞法閉包更容易解釋。

全球scope工作方式不同

Because functions are objects, and in particular because a nested function could be return ed from its enclosing function (producing an object that persists after the call to the enclosing function), Python needs to implement lookup into enclosing scopes differently from lookup into either local or全局范圍。 具體來說,在 3.x 的參考實現中,Python 會將__closure__屬性附加到內部 function,這是一個cell實例的元組,其工作方式類似於引用(在 ZF6F87C9FDCF8B3C3F0Z7F93 的封閉變量意義上)。 (這些也是引用計數垃圾收集意義上的引用;它們使調用幀數據保持活動狀態,以便在封閉的 function return s 之后可以訪問它。)

相比之下,全局查找通過鏈式字典查找來工作:有一個字典實現了全局 scope,如果失敗,則檢查內置 scope 的單獨字典。 (當然,一個全局只寫全局字典,而不是內置字典;沒有builtin關鍵字。)

當然,從理論上講,nonlocal 的實現沒有理由不能依賴全局(然后是內置的) nonlocal中的查找,就像在全局 scope 中的查找回退到內置一樣。 Stack Overflow 不適合推測設計決策背后的原因。 在 PEP 中找不到任何相關內容,因此可能根本沒有考慮過。

我能提供的最好的方法是:與局部變量查找一樣, nonlocal查找通過在編譯時確定變量的 scope 將是什么來工作。 如果您將內置函數視為簡單的預定義、可陰影全局變量(即,實際實現與提前將它們轉儲到全局 scope 之間的唯一真正區別是您可以使用del恢復對內置函數的訪問),那么global查找也是如此。 正如他們所說,“簡單勝於復雜”和“特殊情況不足以打破規則”; 所以,沒有后備行為。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM