Source
《Fluent Python》chapter 8
總結
先上代碼
class Bus1:
def __init__(self, passengers=None):
if passengers is None:
self.passengers = [ ]
else:
self.passengers = list(passengers)
def pick(self, name):
self.passengers.append(name)
def drop(self, name):
self.passengers.remove(name)
class Bus2:
def __init__(self, passengers=[]):
self.passengers = passengers
def pick(self, name):
self.passengers.append(name)
def drop(self, name):
self.passengers.remove(name)
class Bus3:
def __init__(self, passengers=None):
if passengers is None:
self.passengers = [ ]
else:
self.passengers = passengers
def pick(self, name):
self.passengers.append(name)
def drop(self, name):
self.passengers.remove(name)
class Bus4:
def __init__(self, passengers=[]):
self.passengers = list(passengers)
def pick(self, name):
self.passengers.append(name)
def drop(self, name):
self.passengers.remove(name)
上面的代碼實現(xiàn)了幾個校車
類別, 但是只有Bus1
是最優(yōu)實現(xiàn),Bus2, Bus3
都有各自的問題价说。
Bus2
的代碼比Bus1
更簡練辆亏,看起來是更聰明的代碼,但是其問題如下:
Bus2 Error
tBus2
并沒有搭載任何乘客鳖目,但是其乘客列表里卻出現(xiàn)了tBus1
上的乘客Bob
扮叨。這是為什么呢?這是因為在模塊加載的時候领迈,函數(shù)定義的默認值就已經(jīng)創(chuàng)建對象保存在__defaults__
中了彻磁,如下圖所示那么,任何空乘客列表實例化的
Bus2
的self.passengers
都將成為之前函數(shù)定義時創(chuàng)建的默認列表的別名狸捅,也就是說共同指向同一個對象衷蜓,就會出現(xiàn)之前圖像中出現(xiàn)的問題。Bus3
相比Bus1
只在__init__
函數(shù)中的self.passengers=passengers
一句尘喝,而就是這么一句磁浇,造成了如下的錯誤,Bus3 Error
放到現(xiàn)實中朽褪,這是什么錯誤呢置吓,校籃球隊的
Alice
乘坐了一趟校車鞍匾,從校車上下來之后就被籃球隊除名了,Alice
很無奈骑科。這個就是Python
按引用傳遞引起的問題橡淑,傳入的basketball_team
里面的內容被修改了,因為self.passengers
參數(shù)被指定為傳入的passengers
參數(shù)的別名咆爽,所以對self.passengers
也會引起basketball_team
的內容發(fā)生改變梁棠。而
Bus1
的實現(xiàn)中,使用list()
(淺)復制了passengers
對象斗埂,這樣就可以避免出現(xiàn)這種問題了符糊。此外,使用list()
還可以使得傳入的參數(shù)多樣化呛凶,可以是元組男娄、集合或者其他的可迭代對象,并且保證了pick()
和drop()
方法中列表相關函數(shù)使用的合法性漾稀,可謂是非常完美模闲。嗯,對了崭捍,Bus1
這種編程方式叫做防御式編程(defensive programming)
尸折。提一句
Bus4
,是綜合Bus2
和Bus1
給出的一個實現(xiàn)殷蛇,小小測試了一下实夹,并不會出現(xiàn)上述問題橄浓,會不會是一種好的實現(xiàn)呢?
One More Thing
Fluent Python
在豆瓣上評分很高亮航,果然奇書荸实。另外,推薦一下它推薦的神奇網(wǎng)站Python Tutor塞赂,對于理解Python的內部機制很有用泪勒,而且這個網(wǎng)站是開源的,對應的源代碼在github
上宴猾。
感想
感覺高級語言的很多特性的理解還是是基于編譯原理(還沒有學)的圆存,比如說引用和別名,是不是其實在CPU執(zhí)行的時候仇哆,全部已經(jīng)換成了對應對象的內存地址沦辙,對于CPU來說根本就不存在這些東西。