今天正在看書,老鐵給我發來一道面試題。 初看這道題,我覺得是送分題。嘴角上揚寫出一行命令: 仔細看了一眼,這道題說的是將字元刪掉,好險中了他的招,然後改了一下。 看了一下,沒毛病,遂發給老鐵,老鐵瞬即回覆:非也,非也。這種難度豈敢請教愚兄。此題有陷阱,若是生成列表推導或是生成器表達式抑或是map,f ...
今天正在看書,老鐵給我發來一道面試題。
初看這道題,我覺得是送分題。嘴角上揚寫出一行命令:
print(list((i for i in a if '7net' not in i)))
仔細看了一眼,這道題說的是將字元刪掉,好險中了他的招,然後改了一下。
an=(''.join(i.split('7net')) for i in a) print(list(an))
看了一下,沒毛病,遂發給老鐵,老鐵瞬即回覆:非也,非也。這種難度豈敢請教愚兄。此題有陷阱,若是生成列表推導或是生成器表達式抑或是map,filter,三元表達式,都改變了原來的列表生成了新的列表,其實已經不對了。
我去,沒想到一個小小的面試題還暗藏玄機,我試了一下map函數,結果如下:
print(id(a[2])) xx=map(lambda x:x if '7net' not in x else ''.join(x.split('7net')) ,a) print(iter(xx)) print(iter(xx)) print(id(iter(xx)))
可以看出列表的第三個參數的id地址已經變化了,這個值並未包含‘7net’,所以此時列表其實已經不是最初的列表了(就是新列表了)。
看到這裡,我想只能使用列表的刪除方法才能完成原地修改了。
nn= (a.remove(i[1]) for i in list(enumerate(a)) if '7net' in i[1]) print(list(a))
生成器表達式中的remove並未生效,並沒有改變原來的列表。
但是放到列表推導中卻是可以的。
print(id(a)) nn= [a.remove(i[1]) for i in list(enumerate(a)) if '7net' in i[1]] print(list(a)) print(id(a))
print(id(a)) nn=[a.remove(i) for i in a if '7net' in i] print(a) print(id(a))
列表與生成器不同,在for迴圈中修改會導致索引前移,漏刪元素。
但是這並不是我們需要的結果,我們需要的是在原來的基礎上修改字元串而不是移除。
''.join(i.split('7net'),字元串是不可以原地修改的。所以這樣必定會改變列表。
寫到這,我基本上已經斷定了,是不能寫出這道題的了。因為python中的列表實際上是一個元素記憶體地址的存儲序列,列表只是存儲了一個類似指針的地址空間指向內部元素的實際存儲地址。而,我們生成新的字元串就是讓這些記憶體地址指向新的值,而新的值需要開闢新的記憶體空間來存儲,那麼只要換了新的字元串,那麼現在的列表就一定不是原先的列表了。
too young,simple,上面都是我瞎扯的。
晚飯的時候,大佬貼來一段代碼。仔細看了一下,將a淺拷貝,這時a還是之前的所有記憶體地址的序列,將原來的地址一一指向新的數據,那麼列表已經原地修改。
這次看起來應該沒什麼問題了。
print(id(a)) a[:]=map(lambda x:x if '7net' not in x else ''.join(x.split('7net')) ,a) print(id(a)) print(a)
print('0',id(a[0])) print('1',id(a[1])) print('2',id(a[2])) print(id(a)) print('--------------------------') a[:]=map(lambda x:x if '7net' not in x else ''.join(x.split('7net')) ,a) print('0',id(a[0])) print('1',id(a[1])) print('2',id(a[2])) print(id(a)) print(a)
雖然存儲字元串的地址發生了改變,但是存儲列表的地址還是原地址,所以實現了原地修改。
所以正解是這樣的:
a[:]=[i.replace('7net','') if '7net' in i else i for i in a]
是這樣的:
a[:]=map(lambda x:x if '7net' not in x else ''.join(x.split('7net')) ,a)
還是總結一下,沒有簡單的問題,如果你覺得簡單的話,那你真是太簡單了。
歡迎指正我的錯誤。