【原创】面向对象作业:选课系统中用pickle储存多个对象间组合引用关系的那些坑...
?
轉(zhuǎn)載請(qǐng)注明出處:https://www.cnblogs.com/oceanicstar/p/9030121.html
?
想直接看結(jié)論先提前列出:
1、存儲(chǔ)一個(gè)對(duì)象,文件不是真的給你存儲(chǔ)的了對(duì)象這種東西,存儲(chǔ)的都是一些代碼而已。
具體是哪些代碼呢?
想想看,我們保存對(duì)象的目的,是為了方便以后從文件里加載回來(lái)時(shí),能讓計(jì)算機(jī)自動(dòng)幫你構(gòu)建回之前的那個(gè)對(duì)象。那么文件里頭會(huì)存儲(chǔ)一些什么代碼呢?
①要加載文件時(shí),能夠重構(gòu)回之前的那個(gè)對(duì)象,至少能夠?qū)嵗鲞@個(gè)對(duì)象的類的定義代碼得存儲(chǔ)到文件里頭吧
②如果這個(gè)類繼承了一些父類的東西,或者跟其他類有組合關(guān)系之類的blabla…那么這些類的定義代碼也會(huì)儲(chǔ)存到文件里頭
③這個(gè)對(duì)象自己的屬性和方法(在類定義之外自己定義的)得存儲(chǔ)到文件里頭吧
總之,一切目的都是為了重構(gòu)時(shí)找到所有必須的素材(各種類、函數(shù)、變量的定義代碼,還有相互之間的實(shí)例化、引用等關(guān)系),就跟只有集齊七顆龍珠才能召喚神龍一樣……
2、我們將每個(gè)對(duì)象pickle到不同文件后再加載load回來(lái)時(shí),pickle反序列化load加載回來(lái)都是重構(gòu)了一個(gè)原來(lái)對(duì)象的副本,pickle文件里存儲(chǔ)了構(gòu)建出這些對(duì)象需要引用的類、方法、對(duì)象等引用關(guān)系。
3、如果想要加載回來(lái)后的對(duì)象組合關(guān)系還能對(duì)應(yīng)上的話,是不能把這多個(gè)對(duì)象分開(kāi)dump到不同文件的,必要要同時(shí)dump到一個(gè)文件內(nèi)。
4、由于上述原因,實(shí)際應(yīng)用中,我們要儲(chǔ)存多個(gè)對(duì)象間的組合引用關(guān)系,往往需要使用字典/列表/元組等容器來(lái)盛放這些有組合關(guān)系的對(duì)象,然后將這個(gè)容器一次dump到一個(gè)文件中去。
?
——以下故事純屬虛構(gòu),如有雷同,怕是你轉(zhuǎn)載我的吧!轉(zhuǎn)載請(qǐng)注明出處,謝謝!
大海:我X,這選課系統(tǒng)咋這么難寫(xiě)啊,寫(xiě)了我10幾天,老是有bug出來(lái),明明我保存了老師、學(xué)員和班級(jí)間的組合關(guān)系,怎么加載回來(lái)進(jìn)行值的更改,就不會(huì)相互自動(dòng)關(guān)聯(lián)地改變值了呢?
流星:要想講明白pickle儲(chǔ)存多個(gè)對(duì)象之間的組合關(guān)系問(wèn)題,要先從一個(gè)面向?qū)ο蟮睦娱_(kāi)始說(shuō)起。。。
大海:啥例子?
流星:是一個(gè)簡(jiǎn)化了的例子,你看下面
1 class A: 2 def __init__(self, name): 3 self.name = name 4 self.b_list = [] 5 6 7 class B: 8 def __init__(self, name): 9 self.name = name 10 self.a_list = [] 11 12 13 # 各實(shí)例化一個(gè)對(duì)象 14 a1 = A('A類1號(hào)') 15 b1 = B('B類1號(hào)') 16 17 # 打印 2個(gè)實(shí)例各自的列表屬性 18 print('-------當(dāng)前各自的列表屬性-------') 19 print('a1的b_list屬性:', a1.b_list) 20 print('b1的a_list屬性:', b1.a_list) 21 22 # 在實(shí)例b1的列表屬性中建立組合關(guān)系 23 b1.a_list.append(a1) 24 25 # 打印對(duì)象組合關(guān)系 26 print('\n-------當(dāng)前的組合關(guān)系-------') 27 print('a1的b_list屬性:', a1.b_list) 28 print('b1的a_list中的A對(duì)象的b_list屬性:', b1.a_list[0].b_list) 29 30 # pickle序列化保存到 2個(gè)文件里 31 import pickle 32 with open('a1.pk', 'wb') as f1: 33 pickle.dump(a1, f1) 34 with open('b1.pk', 'wb') as f2: 35 pickle.dump(b1, f2)運(yùn)行結(jié)果
-------當(dāng)前各自的列表屬性------- a1的b_list屬性: [] b1的a_list屬性: []-------當(dāng)前的組合關(guān)系------- a1的b_list屬性: [] b1的a_list中的A對(duì)象的b_list屬性: []大海:哈哈,我看懂了,b1的a_list列表屬性添加了a1對(duì)象,建立了組合關(guān)系!
流星:是的,而且我們還把a(bǔ)1對(duì)象和b1對(duì)象分別存到了文件里頭
大海:(滿懷自信地)對(duì)!都用的是pickle序列化dump到文件,再load回來(lái)的話,他們組合關(guān)系肯定還是在的吧!
流星:是嗎?那么讓我們來(lái)驗(yàn)證一下吧
1 import pickle 2 3 class A: 4 def __init__(self, name): 5 self.name = name 6 self.b_list = [] 7 8 9 class B: 10 def __init__(self, name): 11 self.name = name 12 self.a_list = [] 13 14 # 將a1和b1加載回來(lái) 15 with open('a1.pk', 'rb') as f1: 16 a1 = pickle.load(f1) 17 with open('b1.pk', 'rb') as f2: 18 b1 = pickle.load(f2) 19 20 # 打印對(duì)象組合關(guān)系 21 print('-------加載回來(lái)的組合關(guān)系-------') 22 print('a1的b_list屬性:', a1.b_list) 23 print('b1的a_list中的A對(duì)象的b_list屬性:', b1.a_list[0].b_list) 24 25 # 再實(shí)例化一個(gè)b2 26 b2 = B('B類2號(hào)') 27 a1.b_list.append(b2) 28 29 # 打印對(duì)象組合關(guān)系 30 print('\n-------給a1列表屬性添加b2后的組合關(guān)系-------') 31 print('a1的b_list屬性:', a1.b_list) 32 print('b1的a_list中的A對(duì)象的b_list屬性:', b1.a_list[0].b_list)運(yùn)行結(jié)果
-------加載回來(lái)的組合關(guān)系------- a1的b_list屬性: [] b1的a_list中的A對(duì)象的b_list屬性: []-------給a1列表屬性添加b2后的組合關(guān)系------- a1的b_list屬性: [<__main__.B object at 0x00000000025C2A90>] b1的a_list中的A對(duì)象的b_list屬性: []大海:咦?怎么回事?我們不是已經(jīng)建立了b1和a1間的組合關(guān)系嗎?那b1中的a_list里的A類對(duì)象就應(yīng)該是a1啊?
那么我們給a1對(duì)象的b_list屬性列表添加上了對(duì)象b2(上面結(jié)果確實(shí)添加了一個(gè)B object對(duì)象),
同樣的b1中的a_list里的A類對(duì)象的b_list屬性不也應(yīng)該添加上對(duì)象b2了嗎,為啥上面結(jié)果打印結(jié)果還是個(gè)空列表 [ ] 呢?
額……不明白……
到底現(xiàn)在加載回來(lái)的a1對(duì)象,跟b1中的a_list里的那個(gè)A類對(duì)象還是同一個(gè)么?
流星:那我們打印a1對(duì)象和b1的a_list列表屬性看看?
1 # 打印 a1對(duì)象 2 print('\na1對(duì)象:') 3 print(a1) 4 5 # 打印 b1對(duì)象中a_list列表 6 print('b1對(duì)象的a_list列表:') 7 print(b1.a_list)運(yùn)行結(jié)果
a1對(duì)象: <__main__.A object at 0x00000000025F24E0>b1對(duì)象的a_list列表: [<__main__.A object at 0x000000000367D278>]大海:#%&*#@()&%#@,X!……果然不是同一個(gè)對(duì)象了!
怎么搞的,我們不是把a(bǔ)1和b1這2個(gè)對(duì)象都pickle了嗎?組合關(guān)系怎么亂了呢?
流星:稍等片刻,答案即將揭曉。。。來(lái)?xiàng)l華麗的分割線吧
?
大海:這分割線一點(diǎn)都不華麗啊!
流星:……或許等我學(xué)完前端就華麗了吧
大海:……
流星:其實(shí),這里首先要理解的是我們將每個(gè)對(duì)象pickle到不同文件后再加載load回來(lái)時(shí),每個(gè)對(duì)象被恢復(fù)到一個(gè)與原來(lái)對(duì)象值相等的對(duì)象,但本質(zhì)上不是同一個(gè)對(duì)象,而是重構(gòu)了個(gè)新的對(duì)象。
換句話說(shuō),每次pickle反序列化load加載回來(lái)都是原來(lái)對(duì)象的一個(gè)副本,那么我們從把兩個(gè)有組合關(guān)系的對(duì)象a1和b1分別用pickle序列化dump到兩個(gè)文件里頭就是不對(duì)的,這樣加載回來(lái)的時(shí)候,a1和b1對(duì)象的屬性也都是在各自文件load加載過(guò)程中獨(dú)立復(fù)制生成的。
具體來(lái)解釋,就是:
a1對(duì)象的b_list屬性,在a1.pk文件load加載回來(lái)的過(guò)程,可以理解為:a1對(duì)象加載回來(lái)時(shí),計(jì)算機(jī)開(kāi)辟一個(gè)內(nèi)存空間放a1對(duì)象,發(fā)現(xiàn)a1里頭有個(gè)b_list屬性啊,值是空列表[ ],好的,那么給他開(kāi)辟一個(gè)內(nèi)存空間放這個(gè)空列表屬性吧
b1對(duì)象的a_list屬性里頭有值,并且是個(gè)A類的實(shí)例對(duì)象(在保存到文件之前的程序里是a1),而在b1.pk文件load加載回來(lái)的過(guò)程中python重構(gòu)了一個(gè)與A類的實(shí)例對(duì)象(但不是現(xiàn)在的a1了)間的組合關(guān)系,可以理解為:b1對(duì)象加載回來(lái)時(shí),python要重構(gòu)一個(gè)b1對(duì)象,發(fā)現(xiàn)b1里頭有個(gè)a_list屬性啊,值是個(gè)列表,里頭居然還裝了個(gè)A類的實(shí)例對(duì)象,但是這文件里頭沒(méi)有說(shuō)明這個(gè)A類的實(shí)例對(duì)象是誰(shuí)呀,只告訴我了有個(gè)b1對(duì)象要返回,這個(gè)A類的實(shí)例對(duì)象返回給誰(shuí)呢?算了,給b1開(kāi)辟一個(gè)內(nèi)存空間放這個(gè)列表屬性吧,并且去構(gòu)造一個(gè)A類的新的實(shí)例對(duì)象,這樣至少保留了b1的屬性值不變吧,嗯!就這么干!
好了,這下a1對(duì)象和b1對(duì)象都各自加載完成了,但是這樣計(jì)算機(jī)并沒(méi)有把b1對(duì)象的a_list屬性中的A類實(shí)例對(duì)象當(dāng)成是a1來(lái)關(guān)聯(lián)。。。
大海:那么,怎么才能在pickle序列化保存后,a1與b1間的組合關(guān)系還能加載回來(lái)呢?
流星:再來(lái)?xiàng)l華麗的……
大海:……
流星:其實(shí),上面的pickle保存有個(gè)關(guān)鍵問(wèn)題是,有組合關(guān)系的多個(gè)對(duì)象在pickle序列化保存到文件時(shí),如果想要加載回來(lái)后的對(duì)象組合關(guān)系還能對(duì)應(yīng)上的話,是不能把這多個(gè)對(duì)象分開(kāi)dump到不同文件的!
這的原因就像上面解釋的一樣
大海:前面說(shuō)的太啰嗦,我聽(tīng)不懂啊!
流星:……
大海:能簡(jiǎn)單用人話解釋下可以嗎?
流星:好吧,作為神的我就盡量……
大海:……
流星:其實(shí),當(dāng)a1和b1對(duì)象分開(kāi)dump到不同的文件時(shí),加載回來(lái)是分開(kāi)獨(dú)立加載的,因?yàn)?strong>pickle反序列化重構(gòu)對(duì)象間的關(guān)系是在load方法執(zhí)行時(shí)一次性加載回來(lái)生成的,所以在load加載回b1對(duì)象時(shí)(也就是運(yùn)行b1 = pickle.load(f2)時(shí)),就獨(dú)立地把b1對(duì)象的關(guān)系建立好了,即b1的a_list屬性里頭有組合關(guān)系需要關(guān)聯(lián)的對(duì)象的那個(gè)A類的實(shí)例對(duì)象占用的內(nèi)存地址也分配好了,這個(gè)過(guò)程是與運(yùn)行 a1 = pickle.load(f1)相互獨(dú)立的,毫無(wú)關(guān)系,所以load加載回a1對(duì)象的內(nèi)存地址也是另外獨(dú)立分配的,也就是說(shuō),現(xiàn)在加載回來(lái)的b1與a1對(duì)象已經(jīng)沒(méi)有組合關(guān)系了,跟b1有組合關(guān)系的是在運(yùn)行b1 = pickle.load(f2)時(shí),計(jì)算機(jī)在內(nèi)存里自動(dòng)生成的一個(gè)A類的實(shí)例對(duì)象,這個(gè)A類的實(shí)例對(duì)象被放在了b1的a_list屬性列表里頭。
流星:這下明白了吧……?
大海:好像有點(diǎn)點(diǎn)明白了……可是,那我該怎么做才能在文件里加載回a1與b1組合關(guān)系呢?
流星:那就再來(lái)一條華……
大海:別來(lái)了,算我求你了好嘛?
流星:好吧!那就最后再來(lái)一條吧!
大海:……
流星:答案是——把a(bǔ)1與b1dump到同一個(gè)文件里頭!
大海:哦,我知道,pickle是可以多次dump到一個(gè)文件的!我們可以先把a(bǔ)1對(duì)象dump到文件里,然后再把b1對(duì)象也dump到同一個(gè)文件里頭唄,然后load回來(lái)的時(shí)候,load兩次對(duì)吧,我聰明吧?哈哈~
流星:拉倒吧,你去試試看,這樣能還原回來(lái)我們要的組合關(guān)系嗎?
大海:肯定行,你等著……這就運(yùn)行給你看!
1 class A: 2 def __init__(self, name): 3 self.name = name 4 self.b_list = [] 5 6 7 class B: 8 def __init__(self, name): 9 self.name = name 10 self.a_list = [] 11 12 13 # 各實(shí)例化一個(gè)對(duì)象 14 a1 = A('A類1號(hào)') 15 b1 = B('B類1號(hào)') 16 17 # 打印 2個(gè)實(shí)例各自的列表屬性 18 print('-------當(dāng)前各自的列表屬性-------') 19 print('a1的b_list屬性:', a1.b_list) 20 print('b1的a_list屬性:', b1.a_list) 21 22 # 在實(shí)例b1的列表屬性中建立組合關(guān)系 23 b1.a_list.append(a1) 24 25 # 打印對(duì)象組合關(guān)系 26 print('\n-------當(dāng)前的組合關(guān)系-------') 27 print('a1的b_list屬性:', a1.b_list) 28 print('b1的a_list中的A對(duì)象的b_list屬性:', b1.a_list[0].b_list) 29 30 # pickle序列化分兩次dump到 1個(gè)文件里 31 import pickle 32 with open('a1b1.pk', 'wb') as f: 33 pickle.dump(a1, f) 34 pickle.dump(b1, f)運(yùn)行結(jié)果
-------當(dāng)前各自的列表屬性------- a1的b_list屬性: [] b1的a_list屬性: []-------當(dāng)前的組合關(guān)系------- a1的b_list屬性: [] b1的a_list中的A對(duì)象的b_list屬性: []大海:嘿嘿,馬上要load回來(lái)啦,看好了啊!
1 class A: 2 def __init__(self, name): 3 self.name = name 4 self.b_list = [] 5 6 7 class B: 8 def __init__(self, name): 9 self.name = name 10 self.a_list = [] 11 12 13 # 將a1和b1加載回來(lái) 14 import pickle 15 with open('a1b1.pk', 'rb') as f: 16 a1 = pickle.load(f) 17 b1 = pickle.load(f) 18 19 # 打印對(duì)象組合關(guān)系 20 print('-------加載回來(lái)的組合關(guān)系-------') 21 print('a1的b_list屬性:', a1.b_list) 22 print('b1的a_list中的A對(duì)象的b_list屬性:', b1.a_list[0].b_list) 23 24 # 再實(shí)例化一個(gè)b2 25 b2 = B('B類2號(hào)') 26 a1.b_list.append(b2) 27 28 # 打印對(duì)象組合關(guān)系 29 print('\n-------給a1列表屬性添加b2后的組合關(guān)系-------') 30 print('a1的b_list屬性:', a1.b_list) 31 print('b1的a_list中的A對(duì)象的b_list屬性:', b1.a_list[0].b_list) 32 33 # 打印 a1對(duì)象 34 print('\na1對(duì)象:') 35 print(a1) 36 37 # 打印 b1對(duì)象中a_list列表 38 print('\nb1對(duì)象的a_list列表:') 39 print(b1.a_list)運(yùn)行結(jié)果
-------加載回來(lái)的組合關(guān)系------- a1的b_list屬性: [] b1的a_list中的A對(duì)象的b_list屬性: []-------給a1列表屬性添加b2后的組合關(guān)系------- a1的b_list屬性: [<__main__.B object at 0x00000000024A32B0>] b1的a_list中的A對(duì)象的b_list屬性: []a1對(duì)象: <__main__.A object at 0x0000000002571470>b1對(duì)象的a_list列表: [<__main__.A object at 0x0000000002572A90>]大海:我X,怎么還是不行啊!a1對(duì)象和b1.a_list里的那個(gè)A類實(shí)例對(duì)象還是不同!到底要怎么才行啊!啊!啊!
流星:大哥,別雞凍……
大海:啊!啊!啊!解決不了問(wèn)題,我就雞凍!
流星:你雞凍起來(lái)也別拍我行嗎?我都快被你拍死了……
大海:啊!啊!啊!怎么回事啊!
流星:我直接告訴你行了吧= =!
大海:你倒是快講啊!
流星:好好好……那就再來(lái)一條……
大海:%¥&#@*!¥&*
流星:別拍了,不來(lái)了……
大海:快說(shuō)!
流星:其實(shí)答案就是——把a(bǔ)1與b1對(duì)象dump到同一個(gè)文件里頭!……
大海:你大爺,剛剛不就是這么說(shuō)的嗎?
流星:那是因?yàn)槲以掃€沒(méi)說(shuō)完呢,你就是沒(méi)耐性,不等我把話說(shuō)完你就吵著說(shuō)明白了……
大海:那你繼續(xù)說(shuō)完啊!
流星:你別打斷我……除了要把a(bǔ)1與b1對(duì)象dump到同一個(gè)文件里頭,還要保證,是同一次dump命令序列化的
大海:說(shuō)人話!
流星:= =!我的意思就是,可以把a(bǔ)1與b1合并成一個(gè)元組,這樣就可以通過(guò)這個(gè)元組把a(bǔ)1與b1對(duì)象一次性dump到文件里了
大海:我X,這也行啊。。。我怎么沒(méi)想到。。。
流星:是啊,不信我運(yùn)行下你看看。先dump文件……
1 class A: 2 def __init__(self, name): 3 self.name = name 4 self.b_list = [] 5 6 7 class B: 8 def __init__(self, name): 9 self.name = name 10 self.a_list = [] 11 12 13 # 各實(shí)例化一個(gè)對(duì)象 14 a1 = A('A類1號(hào)') 15 b1 = B('B類1號(hào)') 16 17 # 打印 2個(gè)實(shí)例各自的列表屬性 18 print('-------當(dāng)前各自的列表屬性-------') 19 print('a1的b_list屬性:', a1.b_list) 20 print('b1的a_list屬性:', b1.a_list) 21 22 # 在實(shí)例b1的列表屬性中建立組合關(guān)系 23 b1.a_list.append(a1) 24 25 # 打印對(duì)象組合關(guān)系 26 print('\n-------當(dāng)前的組合關(guān)系-------') 27 print('a1的b_list屬性:', a1.b_list) 28 print('b1的a_list中的A對(duì)象的b_list屬性:', b1.a_list[0].b_list) 29 30 # pickle序列化用元組1次dump到同1個(gè)文件 31 import pickle 32 with open('a1b1.pk', 'wb') as f: 33 pickle.dump((a1, b1), f)流星:打印結(jié)果當(dāng)然還是
-------當(dāng)前各自的列表屬性------- a1的b_list屬性: [] b1的a_list屬性: []-------當(dāng)前的組合關(guān)系------- a1的b_list屬性: [] b1的a_list中的A對(duì)象的b_list屬性: []流星:再一次load回來(lái)
1 class A: 2 def __init__(self, name): 3 self.name = name 4 self.b_list = [] 5 6 7 class B: 8 def __init__(self, name): 9 self.name = name 10 self.a_list = [] 11 12 13 # 將a1和b1加載回來(lái) 14 import pickle 15 with open('a1b1.pk', 'rb') as f: 16 a1, b1 = pickle.load(f) 17 18 # 打印對(duì)象組合關(guān)系 19 print('-------加載回來(lái)的組合關(guān)系-------') 20 print('a1的b_list屬性:', a1.b_list) 21 print('b1的a_list中的A對(duì)象的b_list屬性:', b1.a_list[0].b_list) 22 23 # 再實(shí)例化一個(gè)b2 24 b2 = B('B類2號(hào)') 25 a1.b_list.append(b2) 26 27 # 打印對(duì)象組合關(guān)系 28 print('\n-------給a1列表屬性添加b2后的組合關(guān)系-------') 29 print('a1的b_list屬性:', a1.b_list) 30 print('b1的a_list中的A對(duì)象的b_list屬性:', b1.a_list[0].b_list) 31 32 # 打印 a1對(duì)象 33 print('\na1對(duì)象:') 34 print(a1) 35 36 # 打印 b1對(duì)象中a_list列表 37 print('\nb1對(duì)象的a_list列表:') 38 print(b1.a_list)運(yùn)行結(jié)果
-------加載回來(lái)的組合關(guān)系------- a1的b_list屬性: [] b1的a_list中的A對(duì)象的b_list屬性: []-------給a1列表屬性添加b2后的組合關(guān)系------- a1的b_list屬性: [<__main__.B object at 0x00000000025D2A90>] b1的a_list中的A對(duì)象的b_list屬性: [<__main__.B object at 0x00000000025D2A90>]a1對(duì)象: <__main__.A object at 0x00000000025D1470>b1對(duì)象的a_list列表: [<__main__.A object at 0x00000000025D1470>]大海:我去,真的一樣了!牛X!
流星:那必須的!這里同時(shí)把a(bǔ)1,b1對(duì)象dump到一個(gè)文件時(shí),儲(chǔ)存的組合關(guān)系是在這個(gè)文件里頭的,再load出來(lái)返回給新程序的a1和b1對(duì)象,也是直接把組合關(guān)系指定重構(gòu)給了新的a1和b1對(duì)象,沒(méi)有再去單獨(dú)開(kāi)辟內(nèi)存空間去生成其他的對(duì)象了
大海:原來(lái)是這樣,厲害!
流星:哈哈,為了慶祝,再來(lái)?xiàng)l華麗的分割線吧?
大海:來(lái)吧,隨便你來(lái)!
流星:其實(shí)再來(lái)個(gè)分割線是為了再舉2個(gè)pickle序列化的例子的,或許看了下面2個(gè)例子,會(huì)更好地理解這個(gè)問(wèn)題
大海:好!你發(fā)出來(lái),我研究研究
流星:嗯,先看個(gè)列表對(duì)象“遞歸引用”的例子
大海:啥?“遞歸引用”?我瞅瞅……
>>> l = [1, 2, 3] >>> l.append(l) >>> l [1, 2, 3, [...]] >>> l[3] [1, 2, 3, [...]] >>> l[3][3] [1, 2, 3, [...]] >>> p = pickle.dumps(l) >>> l2 = pickle.loads(p) >>> l2 [1, 2, 3, [...]] >>> l2[3] [1, 2, 3, [...]] >>> l2[3][3] [1, 2, 3, [...]]大海:我去,原來(lái)列表還能這么玩啊!?
流星:是啊,看過(guò)“遞歸引用”了,那么也能有點(diǎn)接受下面這個(gè)“循環(huán)引用”的例子了吧……
大海:還有……“循環(huán)引用”……?
>>> a = [1, 2] >>> b = [3, 4] >>> a.append(b) >>> a [1, 2, [3, 4]] >>> b.append(a) >>> a [1, 2, [3, 4, [...]]] >>> b [3, 4, [1, 2, [...]]] >>> a[2] [3, 4, [1, 2, [...]]] >>> b[2] [1, 2, [3, 4, [...]]] >>> a[2] is b 1 >>> b[2] is a 1 >>> f = file('temp.pkl', 'w') >>> pickle.dump((a, b), f) >>> f.close() >>> f = file('temp.pkl', 'r') >>> c, d = pickle.load(f) >>> f.close() >>> c [1, 2, [3, 4, [...]]] >>> d [3, 4, [1, 2, [...]]] >>> c[2] [3, 4, [1, 2, [...]]] >>> d[2] [1, 2, [3, 4, [...]]] >>> c[2] is d 1 >>> d[2] is c 1大海:咦,這個(gè)“循環(huán)引用”也用到了把a(bǔ)、b兩個(gè)列表對(duì)象合并成一個(gè)元組dump到同一個(gè)文件里頭啊
流星:是的!之所以這么做,是因?yàn)?strong>“遞歸引用”和“循環(huán)引用”也類似對(duì)象間的組合關(guān)系,本質(zhì)都是一個(gè)對(duì)象與一個(gè)對(duì)象建立了內(nèi)存地址的引用(遞歸引用是引用自己)關(guān)系。要用元組的形式,同時(shí)一次性將2個(gè)對(duì)象序列化dump到同一個(gè)文件,保留指定的2個(gè)對(duì)象的引用關(guān)系,并且反序列化時(shí)就能把這個(gè)引用關(guān)系把指定返回給重構(gòu)的2個(gè)對(duì)象了。
大海:原來(lái)如此……
流星:嗯,我們可以看看,如果“循環(huán)引用”的例子,改成將a與b分2次dump到1個(gè)文件里頭,結(jié)果會(huì)怎樣?
>>> f = file('temp.pkl', 'w') >>> pickle.dump(a, f) >>> pickle.dump(b, f) >>> f.close() >>> f = file('temp.pkl', 'r') >>> c = pickle.load(f) >>> d = pickle.load(f) >>> f.close() >>> c [1, 2, [3, 4, [...]]] >>> d [3, 4, [1, 2, [...]]] >>> c[2] [3, 4, [1, 2, [...]]] >>> d[2] [1, 2, [3, 4, [...]]] >>> c[2] is d 0 >>> d[2] is c 0流星:你看,這里分2次dump到1個(gè)文件的話,第一次由a對(duì)象單獨(dú)dump進(jìn)文件的字符串信息load回的對(duì)象c,c[2]引用的不再是對(duì)象d了(d是由b對(duì)象dump進(jìn)文件的字符串信息load回的),d[2]引用的也不再是對(duì)象c了,所以a與b本身的相互引用關(guān)系,已經(jīng)在分開(kāi)2次dump時(shí)丟失掉了。那這里c[2],d[2]引用的是誰(shuí)呢?是各自從文件load重構(gòu)成列表對(duì)象c和d時(shí),為了保證c[2]和d[2]的值與之前相等,計(jì)算機(jī)自己用list類重新生成的兩個(gè)新的列表對(duì)象讓他們各自引用
大海:明白了!
流星:最后,推薦個(gè)詳細(xì)講解pickle模塊的博客文章:《python pickle模塊》
轉(zhuǎn)載于:https://www.cnblogs.com/oceanicstar/p/9030121.html
總結(jié)
以上是生活随笔為你收集整理的【原创】面向对象作业:选课系统中用pickle储存多个对象间组合引用关系的那些坑...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Springboot学习问题记录
- 下一篇: ubuntu下进程kidle_injec