Python3中_和__的用途和區別


 

訪問可見性問題

對於上面的代碼,有C++、Java、C#等編程經驗的程序員可能會問,我們給Student對象綁定的nameage屬性到底具有怎樣的訪問權限(也稱為可見性)。因為在很多面向對象編程語言中,我們通常會將對象的屬性設置為私有的(private)或受保護的(protected),簡單的說就是不允許外界訪問,而對象的方法通常都是公開的(public),因為公開的方法就是對象能夠接受的消息。在Python中,屬性和方法的訪問權限只有兩種,也就是公開的和私有的,如果希望屬性是私有的,在給屬性命名時可以用兩個下划線作為開頭,下面的代碼可以驗證這一點。

class Test: def __init__(self, foo): self.__foo = foo def __bar(self): print(self.__foo) print('__bar') def main(): test = Test('hello') # AttributeError: 'Test' object has no attribute '__bar' test.__bar() # AttributeError: 'Test' object has no attribute '__foo' print(test.__foo) if __name__ == "__main__": main()

但是,Python並沒有從語法上嚴格保證私有屬性或方法的私密性,它只是給私有的屬性和方法換了一個名字來“妨礙”對它們的訪問,事實上如果你知道更換名字的規則仍然可以訪問到它們,下面的代碼就可以驗證這一點。之所以這樣設定,可以用這樣一句名言加以解釋,就是“We are all consenting adults here”。因為絕大多數程序員都認為開放比封閉要好,而且程序員要自己為自己的行為負責。

class Test: def __init__(self, foo): self.__foo = foo def __bar(self): print(self.__foo) print('__bar') def main(): test = Test('hello') test._Test__bar() print(test._Test__foo) if __name__ == "__main__": main()

在實際開發中,我們並不建議將屬性設置為私有的,因為這會導致子類無法訪問(后面會講到)。所以大多數Python程序員會遵循一種命名慣例就是讓屬性名以單下划線開頭來表示屬性是受保護的,本類之外的代碼在訪問這樣的屬性時應該要保持慎重。這種做法並不是語法上的規則,單下划線開頭的屬性和方法外界仍然是可以訪問的,所以更多的時候它是一種暗示或隱喻,關於這一點可以看看我的《Python - 那些年我們踩過的那些坑》文章中的講解。

面向對象的支柱

面向對象有三大支柱:封裝、繼承和多態。后面兩個概念在下一個章節中進行詳細的說明,這里我們先說一下什么是封裝。我自己對封裝的理解是“隱藏一切可以隱藏的實現細節,只向外界暴露(提供)簡單的編程接口”。我們在類中定義的方法其實就是把數據和對數據的操作封裝起來了,在我們創建了對象之后,只需要給對象發送一個消息(調用方法)就可以執行方法中的代碼,也就是說我們只需要知道方法的名字和傳入的參數(方法的外部視圖),而不需要知道方法內部的實現細節(方法的內部視圖)。

練習

練習1:定義一個類描述數字時鍾

class Clock(object): """數字時鍾""" def __init__(self, hour=0, minute=0, second=0): """初始化方法   :param hour: 時  :param minute: 分  :param second: 秒  """ self._hour = hour self._minute = minute self._second = second def run(self): """走字""" self._second += 1 if self._second == 60: self._second = 0 self._minute += 1 if self._minute == 60: self._minute = 0 self._hour += 1 if self._hour == 24: self._hour = 0 def __str__(self): """顯示時間""" return '%02d:%02d:%02d' % \ (self._hour, self._minute, self._second) def main(): clock = Clock(23, 59, 58) while True: print(clock) sleep(1) clock.run() if __name__ == '__main__': main()

練習2:定義一個類描述平面上的點並提供移動點和計算到另一個點距離的方法。

from math import sqrt class Point(object): def __init__(self, x=0, y=0): """初始化方法   :param x: 橫坐標  :param y: 縱坐標  """ self.x = x self.y = y def move_to(self, x, y): """移動到指定位置   :param x: 新的橫坐標  "param y: 新的縱坐標  """ self.x = x self.y = y def move_by(self, dx, dy): """移動指定的增量   :param dx: 橫坐標的增量  "param dy: 縱坐標的增量  """ self.x += dx self.y += dy def distance_to(self, other): """計算與另一個點的距離   :param other: 另一個點  """ dx = self.x - other.x dy = self.y - other.y return sqrt(dx ** 2 + dy ** 2) def __str__(self): return '(%s, %s)' % (str(self.x), str(self.y)) def main(): p1 = Point(3, 5) p2 = Point() print(p1) print(p2) p2.move_by(-1, 2) print(p2) print(p1.distance_to(p2)) if __name__ == '__main__': main()

說明:本章中的插圖來自於Grady Booch等著作的《面向對象分析與設計》一書,該書是講解面向對象編程的經典著作,有興趣的讀者可以購買和閱讀這本書來了解更多的面向對象的相關知識。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM