日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > python >内容正文

python

python入口函数的作用_python之函数中参数的作用域

發(fā)布時(shí)間:2023/12/10 python 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python入口函数的作用_python之函数中参数的作用域 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

學(xué)編程究竟學(xué)的是什么呢?在寫文章的這幾天也一直在思考這個(gè)問題——恐怕這也是接下來的幾年一直會去思考的問題。這個(gè)問題的答案也會指導(dǎo)我的方法論,所以索性整頓一下。

現(xiàn)階段我的回答是,發(fā)現(xiàn)需求,然后解決。

最大的需求無非是完成一個(gè)項(xiàng)目,為了做到這一點(diǎn),還有很多需求是完成模塊化的功能,再細(xì)分下去則是要實(shí)現(xiàn)每一個(gè)具體的函數(shù)。這些都做到了,可能整個(gè)功能就跑通了。但是,需求其實(shí)沒有止步:代碼效率能更高么?

魯棒性能優(yōu)化么?

可讀性能改善么?

整體功能的架構(gòu)完善么?

甚至工程細(xì)節(jié),debug效率能更高么?

logger輸出能更合理么?

這些需求則是關(guān)系到積累,可不只是一次項(xiàng)目就能簡單回答的,需要更深的思考和總結(jié)。

所以,在學(xué)習(xí)中怎么貫徹這一點(diǎn)呢?將學(xué)到的知識和項(xiàng)目盡量建立聯(lián)系,以后的知識點(diǎn)的例子我也會盡量從這方面去構(gòu)建。

查看python源碼。很多需求提不出來的原因是見得不夠多,但是源碼里,很多經(jīng)驗(yàn)豐富的前輩不僅預(yù)見了這些需求,還封裝了這個(gè)需求的解決辦法。所以不僅對現(xiàn)階段有幫助,也能是下一階段——如何把問題解決的更高效的預(yù)熱。

下面,我們開始討論面向過程抽象的最基本也是最重要的單位——函數(shù)。

環(huán)境與作用域

在SICP(第三章)中對環(huán)境有著如下定義:一個(gè)環(huán)境是框架(frame)的一個(gè)序列,每個(gè)框架是包含著一些約束的一個(gè)表格(可能為空),這些約束將一些變量的名字關(guān)聯(lián)于對應(yīng)的值(在一個(gè)框架里,任何變量至多只能有一個(gè)約束)。每一個(gè)框架還包含了一個(gè)指針,指向這一框架的外部環(huán)境......

當(dāng)時(shí)走馬觀花的看到了這個(gè)定義,但是在python中debug的時(shí)候才發(fā)現(xiàn)理解了環(huán)境是多么的好用:我們可以通過選取不同的frame,觀看到不同的變量在各個(gè)環(huán)境中是如何變化的。

但是被動的使用肯定是不足夠的,我們還是要主動的洞察變量定義和frame的關(guān)系,更好的預(yù)見我們寫出的代碼真實(shí)的效果是什么。(SICP大法好。。自學(xué)者還是要夯實(shí)基礎(chǔ)。雖然第一次看的時(shí)候似懂非懂,不過這就是積累哦,第二次看到的時(shí)候就有機(jī)會融會貫通了)

變量名解析原則LEGB

其實(shí)上述一大段話,說的是這個(gè)意思:如果當(dāng)前frame里有這個(gè)變量,直接引用,否則去上層frame中查找是否有同名變量,直到找到最高層;若都沒有就報(bào)錯咯~

看個(gè)例子

x = 1

def fun():

print(x)

fun()

# 1

引用好說,但當(dāng)牽扯到賦值的時(shí)候,就不那么顯然了

x = 1

def fun():

x = 1

print(x)

fun()

# 2

這個(gè)還好,比較符合大家直觀感受,但是下面就不一樣了

x = 1

def fun():

x += 1

print(x)

fun()

# UnboundLocalError: local variable 'x' referenced before assignment

既然引用沒錯,這咋不能引用加賦值呢??原來,在函數(shù)內(nèi)部,一旦牽扯到賦值語句,變量就會變成局部變量,像第二個(gè)例子一樣屏蔽掉全局變量x(x=1)。如果想改變?nèi)肿兞?#xff0c;那么就在函數(shù)內(nèi)部事先聲明

def fun():

global x

...

這樣,我們就有一個(gè)比較直觀的感覺:牽扯到在函數(shù)內(nèi)部賦值時(shí),如果是內(nèi)部變量沒什么關(guān)系,但如果改變外部變量的話,一定要像一個(gè)辦法將其引入內(nèi)部空間(一般不是global);相反,如果僅僅是普通引用的話則十分方便,無需過度擔(dān)心。

但是如果函數(shù)嵌套的話會發(fā)生什么情況呢?顯見,最內(nèi)層函數(shù)的外一層就不再是global環(huán)境。具體的層次就是所謂的LEGB。Scope Resolution in Python | LEGB Rule - GeeksforGeeks?www.geeksforgeeks.org

文中小圖清晰的展現(xiàn)了frame的嵌套關(guān)系。留一個(gè)問題:如果import了其他py文件,frame結(jié)構(gòu)又是什么樣子的呢?

frame的存在時(shí)間

最后舉一個(gè)特別精巧的例子,在這個(gè)例子中,我們把函數(shù)作為另一個(gè)函數(shù)的返回值。熟悉數(shù)學(xué)的朋友們知道這個(gè)在數(shù)學(xué)里叫做泛函,是一種強(qiáng)有力的抽象手段。如果有機(jī)會會在SICP中好好討論一下這種所謂的過程的抽象。

def counter():

c = [0]

def inc():

c[0] += 1

return c[0]

return inc

f = counter()

f()

# 1

f()

# 2

f()

# 3

從這個(gè)例子不難看出,局部變量c一直存在在f所代表的frame當(dāng)中。如果g=counter(),則g的計(jì)數(shù)與f毫無關(guān)聯(lián)。

為什么可以c[0] += 1?這是因?yàn)?#xff0c;我們賦值的是變量c所代表的列表中的元素,即,本質(zhì)上,我們對c是引用,所以在local frame中找不到c時(shí),我們能從enclosed frame中找到c拿來引用。

(這個(gè)例子給了我們做計(jì)數(shù)器的巨大的啟發(fā)。

默認(rèn)值的本質(zhì)

def fun(x=[1]):

x.append([2])

print(x)

# 比較下面兩個(gè)輸出

for _ in range(2):

fun()

# [1, 2]

# [1, 2, 2]

for _ in range(2):

fun([1])

# [1, 2]

# [1, 2]

原來,函數(shù)中有這兩個(gè)屬性收集默認(rèn)值:fun.__defaults__ 收集默認(rèn)位置參數(shù)

fun.__kwdefaults_ 收集默認(rèn)關(guān)鍵字參數(shù)

如果我們不明確的賦值,則調(diào)用默認(rèn)值。但是!!因?yàn)槲覀冞@里的默認(rèn)值可變(雖然列表的內(nèi)存地址沒有改變,但是列表的內(nèi)容變了),所以我們的行為也許會修改默認(rèn)值!

只要函數(shù)不被銷毀,作為屬性的默認(rèn)值就會一直記錄所有的改變。

首先,我們要意識到,這種做法有時(shí)有利,有時(shí)有害,不可一概而論;下面我們展示兩種方法:如果一旦我們不想讓默認(rèn)值改變,該采取什么做法。

def fun(x=[]):

x = x[:] # shadow copy

....

第一方面,如果傳入了x,立即對xshadow copy,沒問題;如果沒傳入,我們操作的是默認(rèn)值x的shadow copy,而不是x本身,所以。。。?

這里比較討巧,用了一個(gè)空列表,如果一個(gè)默認(rèn)值很復(fù)雜(其實(shí)不推薦復(fù)雜的默認(rèn)值吧。。),那么shadow copy也是會有shadow copy的問題的對吧?

所以這種做法須謹(jǐn)慎。

def fun(x=None):

if x is None:

x = []

....

哇!這個(gè)方法還是厲害的咧。也是推薦大家使用的。每次調(diào)用,對x重新賦值,肯定能避免這次的改變泄漏到下次操作中。

我們也可以這么理解:

函數(shù)的屬性,和函數(shù)定義本身綁定在一起,存在于global frame(假設(shè)是最外層函數(shù));而每次調(diào)用函數(shù),就會自動生成一個(gè)新的local frame。所以,方法二中的賦值方法是沒有問題的。

總結(jié)

以上是生活随笔為你收集整理的python入口函数的作用_python之函数中参数的作用域的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。