99999久久久久久亚洲,欧美人与禽猛交狂配,高清日韩av在线影院,一个人在线高清免费观看,啦啦啦在线视频免费观看www

熱線電話:13121318867

登錄
首頁精彩閱讀Python中令人頭疼的變量作用域問題,終于弄清楚了
Python中令人頭疼的變量作用域問題,終于弄清楚了
2021-05-21
收藏

來源:Python數(shù)據(jù)之道

作者:大奎

整理:陽哥

大家好,我是陽哥。

學(xué)習(xí)Python變量過程中,曾經(jīng)為變量混亂的作用域問題頭疼不已,全局變量、局部變量、自由變量傻傻分不清,今天來跟大家分享 Python變量作用域 的知識(shí)點(diǎn),文章內(nèi)容由公眾號(hào)讀者 大奎 創(chuàng)作。

我們經(jīng)常聽說Python函數(shù)訪問局部變量、全局變量;在定義裝飾器的時(shí)候,還會(huì)使用自由變量。這些不同的變量是如何賦值、初始化、查找及修改的呢?各自的作用細(xì)則又是什么樣的呢?本篇嘗試解答這個(gè)問題。

Python中的變量名可以指代變量、函數(shù)、類、對象等。一般來說,每個(gè)對象都有一個(gè)變量名指向,更準(zhǔn)確說是 綁定。

作用域的必要性

為啥變量要有作用域呢?

我們在Python里遇到的內(nèi)置、局部、全局及自由變量,就是說變量的作用域。

語言區(qū)分作用域,是為了復(fù)用變量名。引入作用域,相當(dāng)于給變量劃分了各自的“隔離區(qū)”,在不同”隔離區(qū)“里,查找變量變得很容易。

正是因?yàn)橛辛俗饔糜颍覀冊诤瘮?shù)內(nèi)才可以隨意使用變量名,而不擔(dān)心其與全局變量、其他函數(shù)中的變量沖突——因?yàn)檫@兩個(gè)作用域是分割的。

BASIC語言只有全局變量,你能想象嗎?你在一個(gè)函數(shù)里命名的循環(huán)變量i,很可能跟全局變量沖突。寫起程序來,舉步維艱。且會(huì)導(dǎo)致很多修改、檢索問題,維護(hù)很困難。

Python變量定義的時(shí)間和空間

Python 有哪些作用域呢?

Python是動(dòng)態(tài)類型語言,變量是在定義的時(shí)候賦值的。這句話的意思我們分以下幾個(gè)方面來理解:

  • a = 1 賦值時(shí)定義變量
  • from tools import cubie 導(dǎo)入時(shí)定義變量 cubie
  • def fun():pass 定義函數(shù),綁定變量fun
  • def fun(name=None):pass 定義變量name為函數(shù)fun的形式變量(也是局部變量),同時(shí)定義函數(shù),綁定便令fun
  • class Car:pass 定義類,綁定類名Car

以上,我們弄清了變量定義的時(shí)刻,下面來看變量的作用域,也就是變量的活動(dòng)空間怎么規(guī)定出來的。

變量作用域取決于其 定義位置

  • 定義在函數(shù)內(nèi)部的變量、定義在函數(shù)聲明中的形式參數(shù),視為局部變量。
  • 定義在 .py 文件內(nèi)的,且函數(shù)、類之外的變量,視為全局變量。
  • 定義在函數(shù)中,嵌套函數(shù)外,且被嵌套函數(shù)引用的變量,視為自由變量。
  • 定義在builtin中的變量,視為內(nèi)置變量。

面對如此復(fù)雜的四種變量作用域,用一個(gè)例子來說明它們的訪問規(guī)則。

LEGB規(guī)則

四個(gè)作用域遵循LEGB規(guī)則,讓我們用一個(gè)例子來說明。

import builtins

builtins.b = 'builtins' g = 'global' def outer(o1,o2='o2'):
    e = 'enclose'     def inner(i1,i2='i2'):
        print(i1,i2,o1,o2,e,g,b)
    return inner  fun = outer('o1')  fun('i1') 

其輸出為 i1 i2 o1 o2 enclose global builtins

可見,在outer函數(shù)的嵌套函數(shù)inner中的輸出語句 print(i1,i2,o1,o2,e,g,b) 是本程序的重點(diǎn)。其具體執(zhí)行情況如下:

  • print i1和i2,毫無疑問的局部變量。
  • print o1和o2,本地作用域沒有,向上查找到outer函數(shù)形參。形參也為局部變量,所以該變量實(shí)際定義在outer函數(shù)內(nèi),inner這個(gè)內(nèi)嵌函數(shù)外,而inner內(nèi)部引用了這個(gè)變量,所以視為自由變量。
  • print e,本地作用域沒有,類似上例,視為自由變量。
  • print g,本地作用域沒有,自由變量作用域(閉包)沒有,一直上溯到全局作用局找到。
  • print b,本地作用域沒有,自由變量作用域(閉包)沒有,全局作用局沒有,一致上溯到內(nèi)置變量空間找到。

至此,LEGB規(guī)則呼之欲出:在本地空間尋找不到的變量,逐級向上級尋找。這里的LEGB分別指代Local,Enclose,Global和Builtin。

在函數(shù)中讀取賦值全局變量,在內(nèi)嵌函數(shù)中讀取賦值自由變量,會(huì)有一些不同的地方。

nonlocal 和 global

對變量名的賦值和引用,是兩種不同的情況:

  • 賦值:創(chuàng)建一個(gè)變量或者修改。
  • 引用:檢索其值。

以上兩者的差別,會(huì)導(dǎo)致我們在函數(shù)中:

  • 賦值一個(gè)
    • 全局變量:等于創(chuàng)建一個(gè)局部變量。
    • 自由變量:等于創(chuàng)建一個(gè)局部變量。
  • 引用:正常檢索其值。

我們修改上例中的inner函數(shù)為如下形式:

def inner(i1,i2='i2'):     e = 'enclose'     g = 'inner global'     print(i1,i2,o1,o2,e,g,b)

在嵌套函數(shù)內(nèi),重新定義了g變量,其他語言一般理解這是重新賦值全局變量。但是我們看上條規(guī)則:在函數(shù)中,賦值一個(gè)全局變量時(shí),等于創(chuàng)建一個(gè)局部變量。就是說此時(shí)的g已經(jīng)是局部變量了——在程序最后的 print(g) 語句輸出 global,而不是修改后的 inner global 也驗(yàn)證了以上規(guī)則。

完整代碼如下:

import builtins

builtins.b = 'builtins' g = 'global' def outer(o1,o2='o2'):
    e = 'enclose'     g = 'inner global'     def inner(i1,i2='i2'):
        print(i1,i2,o1,o2,e,g,b)
    return inner  fun = outer('o1')  fun('i1') print(g)

輸出結(jié)果如下:

i1 i2 o1 o2 enclose inner global builtins global 

不重新賦值,只是使用全局變量和自由變量,則沒有問題。

自由變量也是類似的情況。

為了解決局部作用域中賦值全局變量和自由變量導(dǎo)致的變成局部變量問題,Python引入關(guān)鍵字 global 和 nonlocal 。

def inner(i1,i2='i2'):     global g
    nonlocal e
    g = 'inner global'     e = 'inner enclose' 

此時(shí)的賦值,則分別是對全局變量和自由變量的操作,而非新建局部變量。

完整代碼如下:

import builtins

builtins.b = 'builtins' g = 'global' def outer(o1,o2='o2'):     e = 'enclose'     def inner(i1,i2='i2'):         global g
        nonlocal e
        g = 'inner global'         e = 'inner enclose'         print(i1,i2,o1,o2,e,g,b)
    return inner 

fun = outer('o1') 
fun('i1')

print(g)

輸出結(jié)果如下:

i1 i2 o1 o2 inner enclose inner global builtins inner global

總結(jié)

  • Python的作用域分為四種,分別是局部、全局、自由和內(nèi)置;
  • 定義變量的位置決定了變量的作用域;
  • 作用域的查找遵守LEGB規(guī)則;
  • 為了在局部作用域中修改全局變量和自由變量,引入了 global 關(guān)鍵字和 nonlocal 關(guān)鍵字。

數(shù)據(jù)分析咨詢請掃描二維碼

若不方便掃碼,搜微信號(hào):CDAshujufenxi

數(shù)據(jù)分析師資訊
更多

OK
客服在線
立即咨詢
客服在線
立即咨詢
') } function initGt() { var handler = function (captchaObj) { captchaObj.appendTo('#captcha'); captchaObj.onReady(function () { $("#wait").hide(); }).onSuccess(function(){ $('.getcheckcode').removeClass('dis'); $('.getcheckcode').trigger('click'); }); window.captchaObj = captchaObj; }; $('#captcha').show(); $.ajax({ url: "/login/gtstart?t=" + (new Date()).getTime(), // 加隨機(jī)數(shù)防止緩存 type: "get", dataType: "json", success: function (data) { $('#text').hide(); $('#wait').show(); // 調(diào)用 initGeetest 進(jìn)行初始化 // 參數(shù)1:配置參數(shù) // 參數(shù)2:回調(diào),回調(diào)的第一個(gè)參數(shù)驗(yàn)證碼對象,之后可以使用它調(diào)用相應(yīng)的接口 initGeetest({ // 以下 4 個(gè)配置參數(shù)為必須,不能缺少 gt: data.gt, challenge: data.challenge, offline: !data.success, // 表示用戶后臺(tái)檢測極驗(yàn)服務(wù)器是否宕機(jī) new_captcha: data.new_captcha, // 用于宕機(jī)時(shí)表示是新驗(yàn)證碼的宕機(jī) product: "float", // 產(chǎn)品形式,包括:float,popup width: "280px", https: true // 更多配置參數(shù)說明請參見:http://docs.geetest.com/install/client/web-front/ }, handler); } }); } function codeCutdown() { if(_wait == 0){ //倒計(jì)時(shí)完成 $(".getcheckcode").removeClass('dis').html("重新獲取"); }else{ $(".getcheckcode").addClass('dis').html("重新獲取("+_wait+"s)"); _wait--; setTimeout(function () { codeCutdown(); },1000); } } function inputValidate(ele,telInput) { var oInput = ele; var inputVal = oInput.val(); var oType = ele.attr('data-type'); var oEtag = $('#etag').val(); var oErr = oInput.closest('.form_box').next('.err_txt'); var empTxt = '請輸入'+oInput.attr('placeholder')+'!'; var errTxt = '請輸入正確的'+oInput.attr('placeholder')+'!'; var pattern; if(inputVal==""){ if(!telInput){ errFun(oErr,empTxt); } return false; }else { switch (oType){ case 'login_mobile': pattern = /^1[3456789]\d{9}$/; if(inputVal.length==11) { $.ajax({ url: '/login/checkmobile', type: "post", dataType: "json", data: { mobile: inputVal, etag: oEtag, page_ur: window.location.href, page_referer: document.referrer }, success: function (data) { } }); } break; case 'login_yzm': pattern = /^\d{6}$/; break; } if(oType=='login_mobile'){ } if(!!validateFun(pattern,inputVal)){ errFun(oErr,'') if(telInput){ $('.getcheckcode').removeClass('dis'); } }else { if(!telInput) { errFun(oErr, errTxt); }else { $('.getcheckcode').addClass('dis'); } return false; } } return true; } function errFun(obj,msg) { obj.html(msg); if(msg==''){ $('.login_submit').removeClass('dis'); }else { $('.login_submit').addClass('dis'); } } function validateFun(pat,val) { return pat.test(val); }