Python面向對象編程Day 25部分
- 2020 年 1 月 19 日
- 筆記
實例化的時候python會自動把實例本身傳給self
__dict__ 查看屬性
類調用的時候不會自動傳遞參數
s1.tell_info()
School.tell_info(s1)
增 .key=什麼;減 del;改 =;查 .調用
類屬性包括數據屬性和函數屬性
實例屬性只有數據屬性
面向對象編程三大特性:多態、繼承、封裝
靜態屬性
說的就是數據屬性,點的方式調用的就是屬性,把函數封裝成數據屬性,使得外部在調用的時候感覺不到內部的邏輯。既可以訪問實例屬性還可以訪問類屬性。
函數前加@property調用的時候不用加括弧,看起來就像數據屬性(例如r1.cal_area,如果函數內部是return,則需要print才能列印)
類方法
函數開頭加@classmethod跟類綁定,變成一個專門跟類有關的方法(取消和實例的捆綁),函數的參數是cls即類名。可以訪問類的數據屬性和函數屬性,但不能訪問實例的屬性。
類調用類方法的時候傳不傳參數?有自動傳遞參數。
類方法用來訪問類屬性。
1 class Foo: 2 x=1 3 # @classmethod 4 def test(cls): 5 print('aaa') 6 f1=Foo() 7 f1.test() 8 Foo.test()
輸出
aaa Traceback (most recent call last): File "F:/PycharmProjects/python_s3/複習練習/practice.py", line 122, in <module> Foo.test() TypeError: test() missing 1 required positional argument: 'cls'
(如果第八行傳入參數Foo,或者第三行去掉注釋列印結果為兩行aaa,不會報異常)
靜態方法
函數前加@staticmethod,跟類和實例無關的操作。名義上的歸屬類管理,不能使用類變數和實例變數,是類的工具包。不能訪問類和實例屬性。
如果在class里單獨寫個函數,前面啥也不加,實例是無法調用它的,這樣寫毫無意義。
組合
大類由一個個小類拼接而成。類跟類之間沒共同點,但是有關聯,用組合實現。
1 class School: 2 def __init__(self,name,addr): 3 self.name=name 4 self.addr=addr 5 6 def zhao_sheng(self): 7 print('%s 正在招生' %self.name) 8 9 class Course: 10 def __init__(self,name,price,period,school): 11 self.name=name 12 self.price=price 13 self.period=period 14 self.school=school 15 16 17 18 s1=School('oldboy','北京') 19 s2=School('oldboy','南京') 20 s3=School('oldboy','東京') 21 22 # c1=Course('linux',10,'1h','oldboy 北京') 23 c1=Course('linux',10,'1h',s1) 24 25 print(c1.__dict__) 26 print(c1.school.name) 27 print(s1)
輸出
{'name': 'linux', 'price': 10, 'period': '1h', 'school': <__main__.School object at 0x0000021A650C99E8>} oldboy <__main__.School object at 0x0000021A650C99E8>
繼承
類的繼承有兩層意義:1.改變 2.擴展
什麼時候用繼承?當類之間有很多相同的功能,提取這些共同的功能做成基類,用繼承比較好
而,當類顯著不同,且較小的類是較大的類所需要的組件時,用組合比較好
類名(參數) #__init__中需要的參數
派生是衍生新的東西,繼承就是延續之前的功能
繼承具有兩種含義:1.繼承基類的方法,並且做出自己的改變或者擴展(減少程式碼重用);2.聲明某子類兼容於某基類,定義一個介面類,子類繼承介面類,並且實現介面中定義的方法。
(第一種含義意義並不大,甚至通常是有害的,因為它使得子類和基類出現強耦合,而程式倡導解耦合;而第二種含義的意義非常重要,叫做介面繼承,介面就是方法(一個具體的函數),子類必須實現父類的方法)
Python的類可以繼承多個類,Java和C#則只能繼承一個類。
經典類繼承(基類沒有任何繼承關係,不繼承object)(python2):深度優先
新式類繼承(python3默認;python2):廣度優先
__mro__生成解析順序元組(經典類沒這個方法) 最後一步找的object說明再往上沒父類了
MRO遵循三條準則:1.子類會先於父類被檢查;2.多個父類會根據他們在MRO元組中的順序被檢查;3.若下一
個類存在兩個合法的選擇,選擇第一個父類

真正的介面繼承要import abc 基類參數寫metaclass=abc.ABCMeta @abc.abstractmethod 確保子類必須實現介面
歸一化,就是只要是基於同一個介面實現的類,那麼所有的這些類產生的對象在使用時,從用法上來說都一樣。
好處在於:
1. 歸一化讓使用者無需關心對象的類是什麼,只需要的知道這些對象都具備某些功能就可以了,這極大地降低了使用者的使用難度。
2. 歸一化使得高層的外部使用者可以不加區分的處理所有介面兼容的對象集合
子類中如何調用父類的方法?(不好用,擴展性差,父類變更之後子類需要變更的地方太多)__init__:在子類的__init__中寫父類名.__init__(包括self在內參數) 方法:vehicle.run(self)
實例化或對象調用方法時會動到self
super().方法(參數,不用傳self)不用寫父類名了
選課系統
1 import pickle #數據傳送模組 2 import hashlib #產生哈希值標識身份 3 import time 4 def create_md5(): 5 m = hashlib.md5() 6 m.update(str(time.time()).encode('utf-8')) 7 return m.hexdigest() 8 9 class Base: 10 def save(self): 11 with open('school.db','wb') as f: 12 pickle.dump(self,f) 13 14 class School(Base): 15 def __init__(self,name,addr): 16 self.id=create_md5() 17 self.name=name 18 self.addr=addr 19 20 class Course(Base): 21 def __init__(self,name,price,period,school): 22 self.id=create_md5() 23 self.name=name 24 self.price=price 25 self.period=period 26 self.school=school 27 28 school_obj = pickle.load(open('school.db', 'rb')) 29 print(school_obj.name,school_obj.addr) 30 # s1=School('oldboy','北京') 31 # s1.save()