從內(nèi)存管理的角度看,Python語言的變量和參數(shù)傳遞情況圖解
概述
從內(nèi)存管理的角度來看,Python中的變量和參數(shù)傳遞有一些特點(diǎn):
☆ 變量是對象的引用:在Python中,變量實(shí)際上是對象的引用,而不是對象本身。當(dāng)給一個(gè)變量賦值時(shí),實(shí)際上是將變量指向了一個(gè)對象。這意味著變量可以指向不同類型的對象,并且可以在程序中隨時(shí)改變指向的對象。
☆ 引用計(jì)數(shù):Python使用引用計(jì)數(shù)來管理內(nèi)存。每個(gè)對象都有一個(gè)引用計(jì)數(shù),表示有多少個(gè)變量引用了該對象。當(dāng)引用計(jì)數(shù)為0時(shí),對象將被自動(dòng)回收。當(dāng)一個(gè)變量不再引用一個(gè)對象時(shí),引用計(jì)數(shù)會(huì)減少。當(dāng)引用計(jì)數(shù)為0時(shí),對象的內(nèi)存將被釋放。
☆ 對象的可變性:Python中的對象分為可變對象和不可變對象??勺儗ο螅ㄈ缌斜?、字典)的值可以被修改,而不可變對象(如整數(shù)、字符串、元組)的值不能被修改。這意味著如果修改了一個(gè)可變對象,那么所有引用這個(gè)對象的變量都會(huì)受到影響。
☆ 參數(shù)傳遞方式:在Python中,函數(shù)的參數(shù)傳遞是按值調(diào)用(call by value) 來傳遞的(其中的 值 始終是對象的 引用 而不是對象的值)——實(shí)際上,按對象引用調(diào)用(call by object reference)調(diào)用這種說法更好。對于不可變對象(如整數(shù)、字符串、元組),由于它們的值不能被改變,所以函數(shù)內(nèi)部對這些對象的修改實(shí)際上是創(chuàng)建了一個(gè)新的對象。因此,函數(shù)內(nèi)部的修改不會(huì)影響到函數(shù)外部的實(shí)際參數(shù)。對于可變對象(如列表、字典),由于它們的值可以被改變,所以函數(shù)內(nèi)部對這些對象的修改會(huì)直接改變原始對象的值。因此,函數(shù)內(nèi)部的修改會(huì)影響到函數(shù)外部的實(shí)際參數(shù)。
在Python中,id()函數(shù)用于獲取對象的唯一標(biāo)識符(即內(nèi)存地址)。每個(gè)對象(Python語言中,所有的 數(shù)據(jù) 都被稱之為 對象)在內(nèi)存中都有一個(gè)唯一的標(biāo)識符,可以通過id()函數(shù)來獲取。
例如id(8.53),它會(huì)返回8.53的唯一標(biāo)識符(內(nèi)存地址)。這個(gè)標(biāo)識符是一個(gè)整數(shù)值,用于唯一標(biāo)識該對象在內(nèi)存中的位置,類似如2200635092656,注意,這個(gè)值在不同的運(yùn)行時(shí)環(huán)境中可能會(huì)有所不同,因?yàn)樗Q于內(nèi)存管理和對象分配的具體實(shí)現(xiàn)。Python的內(nèi)存管理和對象分配模型是自動(dòng)的,由Python的內(nèi)存管理器負(fù)責(zé)。
先看下面情況
解釋:在Python中,id()函數(shù)返回對象的內(nèi)存地址,這是每個(gè)對象的唯一標(biāo)識符。
首先,當(dāng)調(diào)用id(21.37)時(shí),Python創(chuàng)建了一個(gè)浮點(diǎn)數(shù)對象21.37并返回了它的內(nèi)存地址,即2491682696880。如下圖所示:
然后,創(chuàng)建了一個(gè)變量a并將其賦值為21.37。此時(shí),Python再次創(chuàng)建了一個(gè)新的浮點(diǎn)數(shù)對象21.37(因?yàn)楦↑c(diǎn)數(shù)是不可變的),并將a指向這個(gè)新對象。因此,當(dāng)調(diào)用id(a)時(shí),它返回的是這個(gè)新對象的內(nèi)存地址,即2491682692176。如下圖所示:
接下來,創(chuàng)建了一個(gè)新的變量b并將其賦值為a。在這種情況下,Python并沒有創(chuàng)建新的對象,而是讓b指向了a所指向的那個(gè)對象。因此,a和b實(shí)際上是指向同一個(gè)對象,所以id(b)返回的也是同一個(gè)內(nèi)存地址2491682692176。如下所示圖:
最后,當(dāng)打印b時(shí),它顯示的是b所指向的那個(gè)對象的值,即21.37。
提示:Python中變量賦值語句 variable = expression的機(jī)制:
1.計(jì)算表達(dá)式(expression)的值:Python首先會(huì)計(jì)算等號右邊的表達(dá)式的值。
2.創(chuàng)建新對象:Python會(huì)為這個(gè)值創(chuàng)建一個(gè)新的對象。
3.變量(variable)綁定:Python會(huì)將變量名綁定到新創(chuàng)建的對象,或者說變量指向這個(gè)對象。
這是Python變量賦值的基本機(jī)制。需要注意的是,對于某些特定類型的對象——如小整數(shù)(-5到256之間的整數(shù)),和特定短字符串,如比較短的只包含ASCII字符,不包含空格,數(shù)字或者特殊字符的字面量字符串。當(dāng)Python解釋器啟動(dòng)時(shí),它會(huì)預(yù)先創(chuàng)建并緩存一定范圍內(nèi)的整數(shù)和一些常用的短字符串對象。當(dāng)你在代碼中使用這些值時(shí),Python并不會(huì)創(chuàng)建新的對象,而是直接引用已經(jīng)存在的對象。這就是所謂的"interning"機(jī)制。這種機(jī)制可以提高Python的運(yùn)行效率,因?yàn)閷τ谶@些常用的小整數(shù)和短字符串,Python可以避免反復(fù)創(chuàng)建和銷毀對象,從而節(jié)省內(nèi)存和CPU資源。
再看如下情況:
解釋:在Python中,小整數(shù)對象(通常是-5到256之間的整數(shù))是預(yù)先創(chuàng)建并重復(fù)使用的。這是為了優(yōu)化內(nèi)存使用和性能,因?yàn)檫@些小整數(shù)在大多數(shù)Python程序中都會(huì)頻繁使用。所以當(dāng)創(chuàng)建一個(gè)變量a并賦值為2,然后創(chuàng)建一個(gè)變量b并賦值為2,它們實(shí)際上都是指向同一個(gè)內(nèi)存對象。
對于大整數(shù)(通常是大于256的整數(shù)),Python會(huì)為每個(gè)新的整數(shù)創(chuàng)建一個(gè)新的對象。所以當(dāng)創(chuàng)建一個(gè)變量a并賦值為1500,然后創(chuàng)建一個(gè)變量b并賦值為1500,它們實(shí)際上是指向兩個(gè)不同的內(nèi)存對象。這就是為什么id(a)和id(b)返回不同的值。參見下圖:
Python語言的變量和賦值情況
Python語言中,所有的 數(shù)據(jù) 都被稱之為 對象。
每個(gè)整數(shù)、小數(shù)、字符串,后面要學(xué)的 字典、元組、列表 等, 都是對象。
編程語言為了方便操作數(shù)據(jù)對象,也需要給對象起一個(gè)名字,稱之為 變量名 , 有時(shí)也簡稱 變量。
Python中的變量,就是 對象的名稱。
Python中,名稱 用于指代對象。 名稱是通過名稱綁定操作來引入的。
【見官方文檔 命名與綁定
https://docs.python.org/zh-cn/3/reference/executionmodel.html#naming-and-binding
提示:在Python的官方文檔中更多地使用了“名稱”(name)這個(gè)術(shù)語來。但是在實(shí)際的編碼和交流中,人們也常常使用“變量”(variable)這個(gè)術(shù)語來描述Python中的名稱?!?/p>
Python 是一門動(dòng)態(tài)類型的語言,所以我們無須預(yù)先聲明變量類型,直接對變量賦值即可。
可以使用賦值語句來創(chuàng)建變量并使其引用一個(gè)數(shù)據(jù)。當(dāng)一個(gè)變量表示存儲(chǔ)器中的某個(gè)值時(shí),也稱這個(gè)變量引用(reference)了這個(gè)值。
賦值語句的基本格式是:
variable = expression
等號(=)稱為賦值運(yùn)算符(assignment operator)。在這個(gè)基本格式中,variable 是變量名,expression 是一個(gè)值,或者能夠產(chǎn)生一個(gè)值的代碼。賦值語句執(zhí)行結(jié)束后,位于等號左邊的變量將引用位于等號右邊的值。
提示:在給變量賦值之前是不能使用變量的。如果在變量被賦值之前就對它進(jìn)行某種操作,如打印它,將導(dǎo)致一個(gè)錯(cuò)誤。
【注意: Python 語言中的變量與大多數(shù)其他編程語言中的變量的工作方式不同。在大多數(shù)編程語言中,變量是保存值的內(nèi)存位置。在這些編程語言中,當(dāng)給變量賦值時(shí),該值保存在變量的內(nèi)存位置。而在 Python 語言中,變量是一個(gè)內(nèi)存位置,它保存另一個(gè)內(nèi)存位置的地址。將值賦給 Python 變量時(shí),該值將存儲(chǔ)在與該變量分離的內(nèi)存位置。變量保存的是保存值的內(nèi)存位置的地址。這就是為什么在 Python 中,不說變量“保存”一個(gè)值,而說變量“引用”一個(gè)變量?;蛘哒f,Python 中變量是代表存儲(chǔ)在計(jì)算機(jī)存儲(chǔ)器中的某個(gè)值的名稱?!?/p>
變量之所以稱為“變量”,是因?yàn)樵诔绦驁?zhí)行過程中它們可以引用不同的值。下面就看看Python 中變量的賦值和再賦值情況。
創(chuàng)建了一個(gè)名為 dollars 的變量,并將 2.75 賦值給它
Dollars = 2.75
給變量 dollars 賦以一個(gè)新值99.95,當(dāng)給變量 dollars 賦以一個(gè)新值99.95時(shí),雖然舊值 2.75 依然保存在計(jì)算機(jī)的存儲(chǔ)器中,但是因?yàn)闆]有變量引用它,所以這個(gè)值已不能再被使用。參見下圖:
注意,當(dāng)存儲(chǔ)器中的值不再被變量引用時(shí),Python 解釋器將通過所謂的垃圾收集機(jī)制來進(jìn)行處理,自動(dòng)地將它們移出存儲(chǔ)器。
【在python中,變量保存的是對象(值)的引用,id()函數(shù)可以獲取變量在內(nèi)存中的地址。在Python中,值可以放在內(nèi)存的某個(gè)位置(地址),變量用于引用它們,給變量賦一個(gè)新值,原值不會(huì)被新值覆蓋,變量只是引用了新值。順便說明,Python的垃圾回收機(jī)制會(huì)自動(dòng)清理不再被用到的值,所以不用擔(dān)心計(jì)算機(jī)內(nèi)存中充滿被“丟棄”的無效的值。】
一定要牢記:在 Python 語言中,變量僅僅是引用存儲(chǔ)器中某個(gè)數(shù)據(jù)的名字,并且可以引用任何類型的數(shù)據(jù)項(xiàng)。
x = 99 語句創(chuàng)建了名為 x 的變量,并賦給它 int 型的值 99。
將一個(gè)字符串 'Take me to your leader'賦值給變量 x時(shí),變量 x 不再引用一個(gè) int 型數(shù)據(jù),而是引用字符串 'Take me to your leader'。
下面再看看這種情況:
a = 10
b = a
在上述的例子中,我們聲明了一個(gè)變量 a,其值為 10,然后將 b 也指向 a,這是在內(nèi)存中的布局是這樣的,變量 a 和 b 會(huì)指向同一個(gè)對象 10,而不是給 b 重新生成一個(gè)新的對象。
當(dāng)執(zhí)行完 a = a + 10 后,因?yàn)檎麛?shù)是不可變對象,所以并不會(huì)將 10 變成 20,而是生成一個(gè)新的對象 20 ,然后 a 會(huì)指向這個(gè)新的對象。b 還是指向舊對象 10。
Python 允許你同時(shí)為多個(gè)變量賦值。例如:
a = b = c = 10
以面示例,創(chuàng)建一個(gè)整型對象,值為 10,從后向前賦值,三個(gè)變量被賦予相同的數(shù)值。
您也可以為多個(gè)對象指定多個(gè)變量。例如:
a, b, c = 10, 20, " hello"
以面示例,兩個(gè)整型對象 10 和 20 的分配給變量 a 和 b,字符串對象 “hello” 分配給變量 c。
在Python中的參數(shù)傳遞情況
要講清在Python中函數(shù)的參數(shù)傳遞,需要從Python中函數(shù)是什么講起。
在Python中,函數(shù)是一種組織代碼的方式,它將一段程序代碼封裝起來,以便于重復(fù)使用。函數(shù)可以接收輸入?yún)?shù),并返回一個(gè)或多個(gè)結(jié)果。這種封裝可以使代碼更加清晰、易讀,也有助于代碼的重用。Python提供了許多內(nèi)置函數(shù),如print()、len()等,可以直接使用。編程人員還可以自定義函數(shù)。自定義圖示如下:
Python中的函數(shù)定義使用def關(guān)鍵字,后跟函數(shù)名、括號()和冒號。括號中可以放置函數(shù)的參數(shù),參數(shù)之間用逗號分隔。函數(shù)的主體部分在冒號后的縮進(jìn)塊中定義。Python中定義函數(shù)語法如下:
def 函數(shù)名(參數(shù)列表):
??? 函數(shù)體
函數(shù)體由縮進(jìn)的代碼塊組成。Python的函數(shù)體包括:
函數(shù)體包括以下幾個(gè)部分:
文檔字符串(docstring):文檔字符串是可選的,用于描述函數(shù)的作用、參數(shù)、返回值等信息。它位于函數(shù)定義的第一行或第二行,使用三引號('''或""")括起來。文檔字符串可以通過help()函數(shù)或在交互式環(huán)境中使用__doc__屬性來訪問。
語句(statements):函數(shù)體中可以包含任意數(shù)量的語句,用于執(zhí)行具體的任務(wù)。這些語句可以是賦值語句、條件語句、循環(huán)語句等,根據(jù)函數(shù)的需求編寫。
返回表達(dá)式(return expression):函數(shù)可以通過return語句返回一個(gè)值。返回值是可選的,如果沒有return語句或者return后面沒有表達(dá)式,函數(shù)將返回None。當(dāng)函數(shù)執(zhí)行到return語句時(shí),函數(shù)將立即結(jié)束,并將返回值傳遞給調(diào)用函數(shù)的地方。
下面介紹python參數(shù)傳遞
python官方文檔的說法【https://docs.python.org/zh-cn/3/tutorial/controlflow.html#defining-functions 】實(shí)參是使用 按值調(diào)用(call by value) 來傳遞的(其中的 值 始終是對象的 引用 而不是對象的值)——實(shí)際上,按對象引用調(diào)用(call by object reference)調(diào)用這種說法更好。具體來說:
如果傳入的參數(shù)是不可變類型(如數(shù)字、字符串、元組),那么在函數(shù)體內(nèi)修改參數(shù)的值,并不會(huì)影響到原來的變量。因?yàn)椴豢勺冾愋偷淖兞繉?shí)際上是值的引用,當(dāng)試圖改變變量的值時(shí),相當(dāng)于是在創(chuàng)建新的對象。例如:
def change_number(num):
num = 100
x = 10
change_number(x)
print(x) # 輸出:10
在上面的例子中,盡管在函數(shù)內(nèi)部num的值被改變了,但是原變量x的值并沒有改變。參見下圖:
如果傳入的參數(shù)是可變類型(如列表、字典),那么在函數(shù)體內(nèi)修改參數(shù)的值,會(huì)影響到原來的變量。因?yàn)榭勺冾愋偷淖兞看鎯?chǔ)的是一個(gè)地址,當(dāng)試圖改變變量的值時(shí),實(shí)際上是在改變這個(gè)地址所指向的內(nèi)容。例如:
def change_list(lst):
lst.append(100)
x = [1, 2, 3]
change_list(x)
print(x) # 輸出:[1, 2, 3, 100]
在上面的例子中,函數(shù)內(nèi)部對參數(shù)lst的修改影響到了原變量x的值。參見下圖:
python的參數(shù)傳遞機(jī)制官方說法:按對象引用調(diào)用(call by object reference)。調(diào)用者和被調(diào)用者之間共享這個(gè)對象,而對于不可變對象,由于并不能真正被修改,因此,修改往往是通過生成一個(gè)新對象然后賦值來實(shí)現(xiàn)的。
?下面是官網(wǎng)文檔節(jié)選【 https://docs.python.org/zh-cn/3/tutorial/controlflow.html#defining-functions 】
在調(diào)用函數(shù)時(shí)會(huì)將實(shí)際參數(shù)(實(shí)參)引入到被調(diào)用函數(shù)的局部符號表中;因此,實(shí)參是使用 按值調(diào)用(call by value) 來傳遞的(其中的 值 始終是對象的引用 而不是對象的值)[1] 。
[備注1]實(shí)際上,按對象引用調(diào)用(call by object reference) 這種說法更好,因?yàn)?,傳遞的是可變對象時(shí),調(diào)用者能發(fā)現(xiàn)被調(diào)者做出的任何更改(插入列表的元素)。
又:如何編寫帶有輸出參數(shù)的函數(shù)(按照引用調(diào)用)?【見https://docs.python.org/zh-cn/3/faq/programming.html#how-do-i-write-a-function-with-output-parameters-call-by-reference 】
請記住,Python 中的實(shí)參是通過賦值(assignment)傳遞的。由于賦值只是創(chuàng)建了對象的引用,所以調(diào)用方(caller)和被調(diào)用方(callee)的參數(shù)名都不存在別名,本質(zhì)上也就不存在按引用調(diào)用(call-by-reference)的方式。通過以下幾種方式,可以得到所需的效果。(下面是例子,略)
解析一下,這意味著:如果你嘗試在函數(shù)內(nèi)部重新賦值一個(gè)新的對象給參數(shù),這個(gè)改變不會(huì)影響到函數(shù)外部的實(shí)際參數(shù)。因?yàn)樵赑ython中,變量名和數(shù)據(jù)是分開的,變量名只是指向數(shù)據(jù)的引用,而不是數(shù)據(jù)本身。然而,你可以通過一些方法達(dá)到類似引用傳遞的效果。例如,你可以傳遞一個(gè)可變對象(如列表或字典),然后在函數(shù)內(nèi)部修改這個(gè)對象。由于對象的引用被傳遞,所以這些修改會(huì)反映在函數(shù)外部。這就是所謂的“共享傳遞”(pass by sharing)。
?
附錄、
Python變量的認(rèn)識理解 https://blog.csdn.net/cnds123/article/details/116768499?
python函數(shù) https://blog.csdn.net/cnds123/article/details/108179769文章來源:http://www.zghlxwxcb.cn/news/detail-737729.html
多種(C++、Java、JavaScript、Python)編程語言參數(shù)傳遞方式介紹 https://blog.csdn.net/cnds123/article/details/132981086文章來源地址http://www.zghlxwxcb.cn/news/detail-737729.html
到了這里,關(guān)于從內(nèi)存管理的角度來看,Python語言的變量和參數(shù)傳遞情況解析的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!