1. 內(nèi)存分析
1.1 程序運(yùn)行方式
Python執(zhí)行一個(gè)程序:程序就從解釋器申請(qǐng)內(nèi)存
Python解釋器:預(yù)加載->demo.py->demo01.pyc文件->內(nèi)存->運(yùn)行demo01.py程序代碼
1.2 內(nèi)存分配
1.2.1棧內(nèi)存
棧內(nèi)存(stack): 讀取、加載。數(shù)據(jù)速度快但不是很穩(wěn)定,適合存放經(jīng)常臨時(shí)分配、經(jīng)?;厥盏臄?shù)據(jù)變量。
1.2.2 堆內(nèi)存
堆內(nèi)存:讀取、加載數(shù)據(jù)較慢;比較消耗資源,但是一旦數(shù)據(jù)存在,數(shù)據(jù)操作比較穩(wěn)定適合存放的數(shù)據(jù):對(duì)象
1.2.3 數(shù)據(jù)區(qū)
方法區(qū)|數(shù)據(jù)區(qū)[data]:專門加載程序運(yùn)行的代碼字節(jié)數(shù)據(jù),方法數(shù)據(jù)、函數(shù)數(shù)據(jù)等等
1.2.4 靜態(tài)存儲(chǔ)區(qū)
靜態(tài)存儲(chǔ)區(qū)|常量區(qū)[static]:專門存放程序中的公共數(shù)據(jù)、靜態(tài)數(shù)據(jù)的內(nèi)存區(qū)域。

1.3不可變數(shù)據(jù)類型|可便數(shù)據(jù)類型
1.3.1 不可變數(shù)據(jù)類型
一般數(shù)據(jù)類型都是不可變數(shù)據(jù)類型,不可變數(shù)據(jù)類型是在定義了數(shù)據(jù)之后,修改變量的數(shù)據(jù),變量不會(huì)修改原來(lái)內(nèi)存的地址的數(shù)據(jù),而是會(huì)指向新的地址,原有的數(shù)據(jù)保留,這樣方便程序中基本數(shù)據(jù)的利用率。
例如:整數(shù)類型:-5->256,在解釋器加載時(shí),已經(jīng)自動(dòng)分配了這些數(shù)字的內(nèi)存,超出-5->256范圍的整數(shù),在一個(gè)代碼塊中申請(qǐng)一次內(nèi)存。
交互模式:一行命令就是一個(gè)代碼塊
IDE模式-開發(fā)工具:一個(gè)模塊就是一個(gè)代碼塊
b = 12
print(id(b))
b = 13
print(id(b))
輸出:
1796173168
1796173200
1.3.3 可變數(shù)據(jù)類型
對(duì)象在內(nèi)存地址中存儲(chǔ)的數(shù)據(jù)可變
a = list() # 堆內(nèi)存中:存在一個(gè)對(duì)象 list(),一個(gè)變量a指向這個(gè)對(duì)象
print(id(a)) # 查看對(duì)象a的內(nèi)存
print(a)
a.append("hello")
print(id(a))
print(a)
輸出:
1357340489992
[]
1357340489992
['hello']
可變類型|不可變類型 思考題:
nums = [12, 13, 14,"python",["hello","world"]]
a = 12
print(id(a),id(nums[0]))
b = "python"
print(id(b),id(nums[3]))
c = ["hello","world"]
print(id(c),id(nums[4]))
輸出:
1796173168 1796173168
1324994797896 1324994797896
1324995705736 1324994820616
1.3 代碼和代碼塊
Python中的最小運(yùn)行單元是代碼塊,代碼塊的最小單元時(shí)一行代碼
思考:a = ‘hello’在內(nèi)存中會(huì)創(chuàng)建幾塊內(nèi)存空間 =======>2

p = Person(‘tom’,18)在內(nèi)存中,會(huì)創(chuàng)建幾塊內(nèi)存空間 ===========>4

1.4 程序內(nèi)存代碼檢測(cè)
為了便于檢測(cè)代碼內(nèi)存使用率,社區(qū)開發(fā)了一個(gè)模塊memory_profile
通過(guò) pip install memory_profiler
使用方法:通過(guò)測(cè)試的函數(shù)或者類型前面添加@profile注解,讓內(nèi)存分析模塊可以直接進(jìn)行代碼進(jìn)行檢測(cè)
from memory_profiler import profile
class Person:
'''自定義類型'''
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
@profile(precision=10)
def main():
'''入口函數(shù)'''
p = Person('tom',18,'男')
print(p)
p2 = Person('tom',18,'男')
print(p)
if __name__ == "__main__":
main
2 操作符號(hào)
2.1 is 和 == 和 isinstance 的使用
1. a is b :判斷兩個(gè)變量a/b,他們指向的對(duì)象是否時(shí)同一個(gè)對(duì)象
2. a == b :判斷兩個(gè)變量a/b,他們指向的對(duì)象的數(shù)據(jù)內(nèi)容是否一致
3.isinstance(a,b)判斷a是否屬于b類型
3 引用|深拷貝|淺拷貝
3.1 引用
如果程序中多個(gè)不同的地方都要使用同一個(gè)對(duì)象,通過(guò)對(duì)象的引用賦值,將同一個(gè)對(duì)象賦值給多個(gè)變量。
應(yīng)用賦值并不會(huì)產(chǎn)生新的對(duì)象,而是讓多個(gè)變量可以共同指向一個(gè)對(duì)象,通過(guò)多個(gè)變量都可以操作同一個(gè)對(duì)象的數(shù)據(jù)。
class Person:
def __init__(self,name,fav):
self.name = name
self.fav = ["籃球","足球"]
a = Person("tom",["羽毛球"])
b = c = a
print(id(a))
print(id(b))
print(id(c))
輸出:
2207567775728
2207567775728
2207567775728
3.2 淺拷貝
復(fù)制一個(gè)對(duì)象,復(fù)制對(duì)象中的屬性數(shù)據(jù)的引用
方法:導(dǎo)入模塊 copy
X = copy.copy(a) #拷貝了a對(duì)象,產(chǎn)生了一個(gè)對(duì)象x
class Person:
def __init__(self,name,fav):
self.name = name
self.fav = ["籃球","足球"]
# a = Person("tom",["羽毛球"])
# b = c = a
# print(id(a))
# print(id(b))
# print(id(c))
import copy
a = Person("tom","lol")
x = copy.copy(a)
x.fav.append('aa')
print(id(a)) #2519924135976
print(id(x)) #2519924136872
print(a.fav) #['籃球', '足球', 'aa']
print(x.fav) #['籃球', '足球', 'aa']
3.3 深拷貝
和對(duì)象的淺拷貝不同,對(duì)象的深拷貝,是對(duì)象數(shù)據(jù)的直接拷貝,而不是簡(jiǎn)單的引用拷貝,主要是通過(guò)python內(nèi)建標(biāo)準(zhǔn)模塊copy提供的deepcopy函數(shù)可以完成對(duì)象深拷貝。
4 垃圾回收機(jī)制
自動(dòng)回收無(wú)效對(duì)象數(shù)據(jù),通過(guò)垃圾回收算法進(jìn)行操作
垃圾回收:Garbage Collection : GC
Python中,以引用計(jì)數(shù)垃圾回收算法為主要回收機(jī)制
以標(biāo)記-清除 和 分代回收為輔助回收機(jī)制
4.1 引用計(jì)數(shù)
1.查詢指定對(duì)象的引用數(shù)量
Import sys
s = sys.getrefcount(p) #查詢p的引用數(shù)量為s
- python是一個(gè)面向?qū)ο蟮娜躅愋驼Z(yǔ)言,所有的對(duì)象都是直接或者間接繼承自object類型,object類型的核心其實(shí)就是一個(gè)結(jié)構(gòu)體對(duì)象
typedef struct_object {
int ob_refcnt;
struct_typeobject *ob_type;
} PyObject;
在這個(gè)結(jié)構(gòu)體中,ob_refcnt就是對(duì)象的引用計(jì)數(shù),當(dāng)對(duì)象被創(chuàng)建或者拷貝時(shí)該計(jì)數(shù)器就會(huì)增加1,當(dāng)對(duì)象的引用變量被刪除時(shí)就會(huì)減少1,當(dāng)引用計(jì)數(shù)為0時(shí),對(duì)象數(shù)據(jù)就會(huì)被釋放。
4.2 標(biāo)記清除
標(biāo)記清除思想: 首先找到python中的一批根節(jié)點(diǎn)對(duì)象,通過(guò)根節(jié)點(diǎn)對(duì)象可以找到他們指向的子節(jié)點(diǎn)對(duì)象,如果搜索過(guò)程中有這個(gè)指向是從上往下的指向,表示這個(gè)對(duì)象是可達(dá)的,否則該對(duì)象是不可達(dá)的,可達(dá)部分的對(duì)象在程序中需要保留下來(lái),不可達(dá)部分的對(duì)象在程序中時(shí)不需要保留的。
4.3 分代回收
在python內(nèi)部處理機(jī)制中,定義了三個(gè)不同的鏈表數(shù)據(jù)結(jié)構(gòu)[第0代,第一代,第二代]。Python為了提高程序執(zhí)行效率,將垃圾回收機(jī)制進(jìn)行了閾值限定,0代回收最為密集,其次是1代,最后是2代
4.4 垃圾回收處理
Python中的gc模塊提供了垃圾回收處理的各項(xiàng)功能機(jī)制,必須import gc才能使用
gc.set_debug(flags):設(shè)置gc的debug日志,一般為gc.DEBUG_LAKE
gc.collect([generation]):顯示進(jìn)行垃圾回收處理,可以輸入?yún)?shù)~參數(shù)表示回收的對(duì)象代數(shù),0表示只檢查第0 代對(duì)象,1表示檢查第0、1代對(duì)象,2表示檢查0,1,2代對(duì)象,如果不傳遞參數(shù),執(zhí)行FULL COLLECT,也就是默認(rèn)傳遞2
gc.set_threshold(threshold0[,threshold2[,threshold3]]):設(shè)置執(zhí)行垃圾回收機(jī)制的頻率
gc.get_count():獲取程序?qū)ο笠玫挠?jì)數(shù)器
gc.get_threshold():獲取程序自動(dòng)執(zhí)行GC的引用計(jì)數(shù)閾值
在程序開發(fā)過(guò)程中需要注意:
l 項(xiàng)目代碼中盡量避免循環(huán)引用
l 引入gc模塊,啟用gc模塊自動(dòng)清理循環(huán)引用對(duì)象的機(jī)制
l 將需要長(zhǎng)期使用的對(duì)象集中管理,減少GC資源消耗
l gc模塊處理不了重寫del方法導(dǎo)致的循環(huán)引用,如果一定要添加該方法,需要顯式調(diào)用gc模塊的garbage中對(duì)象的del方法進(jìn)行處理
`