鍍金池/ 問答/Python/ python 全局變量

python 全局變量

題目描述

函數(shù)內(nèi)全局變量引用的理解

題目來源及自己的思路

自學(xué)中發(fā)現(xiàn)的理解問題

相關(guān)代碼

// 請把代碼文本粘貼到下方(請勿用圖片代替代碼)

c = 1
def test():
    c= c+1
    print(c)
test()

錯誤:輸出c未被初始化


c = 1
def test():
    a= c+1
    print(a)
test()

輸出:2

個人理解不知道正確否

python賦值,先進行=右邊的計算,進行了c的引用,再進行了c的賦值,就報錯了,變量必須先賦值在引用。
和全局掛鉤理解是不是:
一般情況下:函數(shù)內(nèi)部可以調(diào)用全局的引用,但不能對其賦值, 除非global,nonlocal或者參數(shù)傳入

回答
編輯回答
九年囚

函數(shù)作用域問題,因為c += 1 c是函數(shù)內(nèi)局部變量,所以就會有c未定義。
同一作用域下的同一變量,不能來自兩個作用域。
c =c + 1。無法要求等號左右兩側(cè),來自不同作用域。
代碼不能具有二異性

2017年11月6日 10:25
編輯回答
情未了

理解沒問題

如果直接打印C+1是可以的。

c = 1
def test():
    print(c+1)
test()

2

使用了golbal會使得全局變量也發(fā)生變化

c = 1
def test():
    global c
    c = c + 1
    print(c)
test()
print(c)

2
2

2018年2月7日 21:17
編輯回答
懷中人

理解的都沒問題。但我想聊聊出現(xiàn)這種情況的原因。在講原因之前,需要先知道python中變量的搜索順序,這個順序是 LGB (不考慮閉包情況)即local本地,global全局,builtin內(nèi)建。比如:

a = 1
def test():
    a = 3
    print(a)
test()

函數(shù)內(nèi)聲明了局部變量 a ,在打印中使用,在本地環(huán)境中命中,因此使用的是 3。

也許你會問這個知識點我早就知道了,這和本問題有什么關(guān)系呢?

有的,難道你不覺得奇怪嗎?報錯是變量未初始化,而不是變量未定義。

題目中函數(shù)內(nèi) c= c+1 就已經(jīng)表明了聲明的變量 c 是屬于局部變量的。 按理說,先執(zhí)行賦值語句右側(cè),而此時 c 并沒有聲明,應(yīng)該在全局環(huán)境命中才對啊。所以想象中的結(jié)果應(yīng)該是局部變量 c = 2 而全局變量的 c 保持原值。

但是,這只都是想當(dāng)然。講了這么多其實我是想引出,python雖然是動態(tài)語句,但它還是會對代碼做掃描工作的,會有收集有用的靜態(tài)信息。函數(shù)的應(yīng)該信息會放在 code 對象中,里面的信息就包含了局部變量名稱的集合,可以通過 co_varnames 得到,如下:

c = 1
def test():
    c= c+1
    a = 3   # 另一個局部變量
    print(c)

print(test.__code__.co_varnames)    # ('c', 'a')

因此,函數(shù)test在執(zhí)行前,變量 c 就已經(jīng)被聲明在局部變量環(huán)境中了,而不是我們自認為的當(dāng)賦值語句運行后才會在局部變量里。于是,這就導(dǎo)致了報錯信息是變量未初始化而不是變量未定義。

====== 分割線 =========
題主評論要求:

關(guān)于變量的初始化,定義,創(chuàng)建這三者關(guān)系,能幫忙疏導(dǎo)一下理解嗎?

這三個意思基本差不多,沒必要分得太開。把變量環(huán)境理解成一個字典 name_env = dict() 其實就很好理解了(事實上python底層也確實是這樣處理的)。對于在這個環(huán)境內(nèi)要創(chuàng)建一個名為 a 的變量,就可以是 name_env['a'] = value 的形式了。

這個過程也就是賦值語句形如 a = value 時會調(diào)用賦值的指令 STORE_NAME 。我們看一下這個賦值過程你就理解了。
這部分代碼在 ceval.c 中,詳見 ceval.c

TARGET(STORE_NAME) {
  PyObject *name = GETITEM(names, oparg);
  PyObject *v = POP();
  PyObject *ns = f->f_locals;
  int err;
  if (ns == NULL) {
      PyErr_Format(PyExc_SystemError,
                   "no locals found when storing %R", name);
      Py_DECREF(v);
      goto error;
  }
  if (PyDict_CheckExact(ns))
      err = PyDict_SetItem(ns, name, v);
  else
      err = PyObject_SetItem(ns, name, v);
  Py_DECREF(v);
  if (err != 0)
      goto error;
  DISPATCH();
}

代碼不多,可以逐個分析下,第一行獲得的 name 就是賦值語句 a = value 的 a,a以python類型 str 形式存在。第二行 v 從棧中獲取,也就是 value 的值。第三行 ns 是從幀對象中獲得局部變量環(huán)境(大多數(shù)情況下是個字典類型,如果幀環(huán)境不在函數(shù)或類中,取得的是全局變量環(huán)境)。PyDict_SetItem(ns, name, v);PyObject_SetItem(ns, name, v); 就可以理解為 ns[name] = vsetattr(ns, name, v) 了,創(chuàng)建過程就是這樣了。

變量究竟是創(chuàng)建還是初始化還是覆蓋已有的變量值,其實底層并不關(guān)心。

回到本地中,本地變量的符號表會保存在靜態(tài)信息里面,我猜測搜索變量時有優(yōu)先去靜態(tài)信息中得到信息,來更快的知道變量應(yīng)該是在局部還是全局中查找吧。

2017年4月2日 01:36
編輯回答
話寡
  • 內(nèi)部函數(shù) 不修改 全局變量可以訪問全局變量
  • 內(nèi)部函數(shù) 修改 同名全局變量,則 python 會認為它是一個局部變量

即: 如果在函數(shù)中對全局變量進行賦值修改, 就會出現(xiàn) Unbound-LocalError.

注: 不僅是對于 全局變量 是這樣, 對于所有 作用域的 作用域都是如此, 如嵌套函數(shù)等.

當(dāng)然,你可以用 global 聲明那個變量是全局的。

2017年11月9日 04:28