面向對象

面向對象 Object Oriented

1、概述

  面向對象是一種對現實世界理解和抽象的方法,是計算機編程技術發展到必定階段後的產物。面向對象編程(OOP)具體說是一種程序開發方法,一種編程思想、範式。你能夠粗濾的理解爲,項目經理乾的活就是面向對象,負責分配任務,而coder乾的活就是面向過程,負責具體任務的實施細節。
python

1.面向過程:分析出解決問題的具體細節、步驟,而後逐步實現。

  1. 公式:程序 = 算法 + 數據結構
  2. 優勢:全部環節、細節本身掌控。
  3. 缺點:考慮全部細節,工做量大。
  4. 找出解決問題的人,而後分配職責。

2.面向對象:找出解決問題的人,而後分配職責,不關注實施細節。

1.公式:程序 = 對象 + 交互程序員

2.優勢算法

(1) 思想層面:編程

-- 可模擬現實情景,更接近於人類思惟。python3.x

-- 有利於梳理概括、分析解決問題。數據結構

(2) 技術層面:app

-- 高複用:對重複的代碼進行封裝,提升開發效率。函數

-- 高擴展:增長新的功能,不修改之前的代碼。工具

-- 高維護:代碼可讀性好,邏輯清晰,結構規整。學習

3.缺點:學習曲線陡峭。

2、類和對象

1.類(class):具備相同屬性(實例變量)和行爲(實例方法)的對象的抽象。實際上就是一種自定義的數據類型,類也有對應的類變量和類方法。

2.對象(instance):類的具體實例,即歸屬於某個類別的」個體」。

3.屬性和行爲:類與類方法不一樣,對象與對象屬性值不一樣

1.類和對象的建立

(1)建立類

1.格式

class 類名(object):

「」」文檔說明」」」

def _init_(self,參數列表):

self.實例變量名= 參數(形參)

def  實例方法名(self,參數列表):

pass

2.說明

--   類名全部單詞首字母大寫,多個單詞不用下劃線隔開採用駝峯體。

-- (object)是表示該類是從object類繼承下來的,python3.x版本可省略

-- __init__也叫構造函數,建立對象時被調用,也能夠省略。

-- self綁定的是被建立的對象的內存地址,名稱能夠隨意。

拓展:object類和type類的區別,即基類與元類的區別

1.object類是繼承層面的:其餘的類或者對象都是經過繼承的關係,直接或者間接的繼承了object,翻閱全部對象的族譜,最後必定會發現它們的老祖宗就是object。

1 >>>list.__base__    #Python爲全部類都提供了一個__bases__屬性,經過該屬性能夠查看該類的全部直接父類,該屬性返回全部直接父類組成的元組
  (<class 'object'>,)
2 >>>type.__bases__
  (<class 'object'>,)
3 >>>object.__bases__   #object類是全部類的基類
  ()

2.type類是類建立對象層面的:python中一切皆對象,數據、對象是由str、list等數據類型和自定義類建立的,而全部數據類型和自定義類都是由元類建立的

1 >>> type(list)
  <class 'type'>
2 >>> type(object)
  <class 'type'>
3 >>> type(type)
  <class 'type'>    #type類是全部類的元類,可是其自己是由虛擬機建立的

(2)建立對象(實例化)

1.格式

變量 = 類名 (實參數列表)

2.說明

--  python中無處不對象,對象是python中對數據的一種抽象的表示

 

--  全部對象都有三種特性:id、類型、值

id:對象的內存地址,由變量關聯,查看經過:id(變量)

類型:生成對象的模型,對應類名,查看經過:type(變量)

值:對象中存放的數據,對應實參列表,查看經過:變量.__dict__

 

--  每一個對象都是由其對應的類建立出來的

拓展:與對象的建立,調用,刪除相關的魔法方法:__new__()方法,__init__()方法,__call__()方法,__del__()方法

1.__new__()方法和__init__()方法:__new__()方法是一個類方法,在對象被建立的時候調用,該方法負責建立(實例化)一個對象。若是建立對象成功,該對象會自動的調用__init__()方法,對自身進行初始化,若是建立對象失敗,沒有對象了,天然也就沒誰去調用__init__()方法了。

2.__call__()方法:若是類中定義了該方法,可使對象成爲像函數和類同樣的可調用對象。

 1 class Entity:
 2     '''
 3     調用實體來改變實體的位置。
 4     '''
 5 
 6     def __init__(self, size, x, y):
 7         self.x, self.y = x, y
 8         self.size = size
 9 
10     def __call__(self, x, y):
11         '''改變實體的位置'''
12         self.x, self.y = x, y
13 
14 
15 e = Entity(1, 2, 3)  # 建立實例
16 print(e.__dict__)  # {'x': 2, 'y': 3, 'size': 1}
17 e(4, 5)  # 實例能夠象函數那樣執行,並傳入x y值,修改對象的x y
18 print(e.__dict__)  # {'x': 4, 'y': 5, 'size': 1}

3.__del__()方法:是python垃圾回收機制的實際應用,當類中定義了該方法時,建立的對象的引用計數爲0時該方法被調用

>>> class D(object):
    def __init__(self):
        print 'this is D.__init__()'
    def __del__(self):
        print 'this is D.__del__()'

>>> d = D()
this is D.__init__()
>>> d2 = d
>>> d3 = d
>>> 
>>> del d
>>> del d2
>>> del d3   #當對象的引用計數爲0時自動觸發__del__()方法
this is D.__del__()

 

2.實例成員

(1)實例變量

1.語法

(1) 定義:對象地址.變量名=數據值

(2) 調用:對象地址.變量名=數據值

2.說明

(1) 首次經過對象賦值爲建立,再次賦值爲修改。

(2) 一般在__init__構造函數中(self.實例變量名=形參)建立,建立對象時賦值。也能夠在

(3) 每一個對象存儲一份,經過對象地址訪問

 3.做用:描述對象自身的數據。

 4.__dict__對象的屬性,用於存儲自身實例變量(包括私有化變量)的字典。

(2)實例方法

1.語法

(1) 定義: def  方法名稱(self, 形參列表):

方法體

(2) 調用: 對象地址.實例方法名(實參列表)

2.說明

(1) 定義時至少有一個形參,通常命名爲"self",用於綁定調用這個方法的對象地址,經過對象調用實例方法時self不用傳參

(2)每一個對象共用一份,經過對象地址訪問。不建議經過類名訪問實例方法(對象與對象數據值不一樣,經過類名調用實例方法時,須要給self參數傳參具體的對象)

3.做用:表示對象行爲。

 

 1 """
 2     實例成員
 3         記住一句話:實例成員,使用對象地址訪問.
 4 """
 5 class Student:
 6     pass
 7 
 8 s01 = Student()
 9 # 定義實例變量: 對象.變量名 = 數據值
10 s01.name = "lennie"
11 print(s01.name)  #"lennie"
12 s02 = Student()
13 # print(s02.name)# AttributeError,由於s02指向的對象,沒有建立過實例變量name
14 # 經過__dict__獲取當前對象的全部實例變量
15 print(s01.__dict__)  #{'name': 'lennie'}
16 print(s02.__dict__)  #{}
17 
18 
19 class Student02:
20     def __init__(self, name, height):
21         self.name = name
22         self.height = height
23 
24     # 實例方法
25     def print_self(self):
26         print(self.name,self.height)
27 
28 
29 s01 = Student02("lennie", 170)
30 s02 = Student02("ginger", 162)
31 # 建議:實例方法,經過對象地址訪問.
32 s01.print_self()
33 # 不建議:Student02.print_self(),若沒有傳遞對象地址,實例方法不能正確訪問對象數據.
34 Student02.print_self(s02)

 

3.類成員

(1)類變量

1.語法

(1) 定義:在類中,方法外定義變量。

class 類名:

   變量名 = 表達式

(2) 調用:類名.變量名

      不建議經過對象訪問類變量

2.說明

(1) 存儲在類中。

(2)全部對象共享一份,可經過類或者對象直接調用

3.做用:描述全部對象的共有數據。

(2)類方法

1.語法

(1) 定義:

    @classmethod

    def 方法名稱(cls,形參列表):

         方法體

(2) 調用:類名.方法名(實參列表)

      不建議經過對象訪問類方法

2.說明

(1) 至少有一個形參,通常命名爲'cls',用於綁定類的內存地址。

(2) 使用@classmethod修飾的目的是調用類方法時能夠隱式傳遞類,即經過類名調用類方法時cls不須要傳參。

(3) 類方法中不能訪問實例成員,實例方法中能夠訪問類成員

3.做用:操做類變量。

 

 1 """
 2     類成員,用類的地址去訪問
 3 """
 4 class ICBC:
 5     # 類變量:總行的錢
 6     total_money = 1000000
 7     # 類方法
 8     @classmethod
 9     def print_total_money(cls):
10         # print(id(cls), id(ICBC))
11         # cls : 存儲當前類的地址
12         # print("當前總行金額:",ICBC.total_money)
13         print("當前總行金額:", cls.total_money)
14 
15     def __init__(self, name, money):
16         self.name = name
17         self.money = money
18         # 從總行扣除當前支行的錢
19         ICBC.total_money -= money
20 
21 
22 i01 = ICBC("天壇支行", 100000)
23 i02 = ICBC("陶然亭支行", 100000)
24 # 主流:經過類訪問類成員
25 ICBC.print_total_money()  #"當前總行金額: 800000"
26 print(ICBC.total_money)   #800000
27 # 非主流:經過對象訪問類成員
28 # print(i02.total_money)
29 # i02.print_total_money()

 

(3)靜態方法

1.語法

(1) 定義:

    @staticmethod

    def 方法名稱(形參列表):

            方法體

(2) 調用:類名.方法名(實參列表)

      不建議經過對象訪問靜態方法

2.說明

(1) 使用@ staticmethod修飾的目的是該方法不須要隱式傳參數,即參數列表不含selfcls參數,其實就是個函數

(2) 靜態方法不能訪問實例成員和類成員

3.做用:定義經常使用的工具函數

3、三大特徵

1.封裝

(1)數據角度講

1.定義:將一些基本屬性封裝成共有屬性,類的建立中的__init__初始化

2.優點:將數據與對數據的操做相關聯,代碼可讀性(相比容器)更高(類是對象的模板)。

(2)行爲角度講

  1. 定義:類外提供必要的功能,隱藏實現的細節,私有化操做

  2. 優點:簡化編程,使用者沒必要了解具體的實現細節,只須要調用對外提供的功能。

  3. 私有成員:

(1) 做用:只有類內部能夠直接訪問,外部不能直接訪問,能夠對參數作檢查,避免傳入無效的參數

(2) 作法:__成員名

(3) 本質:障眼法,實際也能夠訪問。私有成員的名稱被修改成:_類名__成員名,能夠經過_dict_屬性或dir函數查看。

 1 class Coder:
 2     """
 3     私有成員的讀和寫操做
 4     """
 5     def __init__(self, name, age):
 6         self.name = name
 7         self.__age = age  #私有化方式一:私有成員命名採用雙下劃線開頭
 8         # self.set_age(age)  #私有化方式二
 9 
10     # 使用兩個公開的方法獲取和修改私有屬性
11     def get_age(self):
12         return self.__age  # 類內部訪問無限制
13 
14     def set_age(self, value):
15         if 0 <= value <= 100:  # 檢驗數據的有效性,限制隨意更改屬性值
16             self.__age = value
17         else:
18             raise ValueError
19 
20     age=property(get_age,set_age)  #無讀寫限制
21     # age=property(get_age,None)  #寫限制
22     # age=property(None,set_age)  #讀限制
23     # age=property(None,None)  #讀寫均限制
24 
25 c = Coder("lennie", 26)
26 # 1.私有成員不能直接訪問
27 print(c.__age)  #AttributeError: 'Coder' object has no attribute '__age'
28 print(c.__dict__)  # {'name': 'lennie', '_Coder__age': 26}
29 # 2.可經過 對象._類名__成員名 訪問(此種訪問無限制)
30 c._Coder__age = 27
31 print(c._Coder__age)  # 27
32 # 3.可經過兩個私有方法實現私有成員的讀寫(此種方式可限制私有成員被隨意改寫)
33 c.set_age(30)
34 print(c.get_age())  # 30
35 #4.可經過設置property(讀方法名,寫方法名),實現私有成員的讀寫
36 c.age=32
37 print(c.age)  #32

4.屬性@property

公開的實例變量,缺乏邏輯驗證。私有的實例變量與兩個公開的方法相結合,又使調用者的操做略顯複雜。而屬性能夠將兩個方法的使用方式像操做變量同樣方便。

(1) 定義:

@property             #容許讀,等價於定義類變量:屬性名=property(讀方法名,None)

 

def 屬性名(self):

return self.__屬性名

@屬性名.setter              #容許寫,等價於定義類變量:屬性名=property(讀方法名,None)

def 屬性名(self, value):

self.__屬性名= value

(2) 調用:

對象.屬性名 = 數據

變量 = 對象.屬性名

(3) 說明:

  • 一般兩個公開的屬性,保護一個私有的變量。
  • @property 負責讀取,@屬性名.setter 負責寫入
  •  只寫:屬性名= property(None, 寫入方法名)
 1 class Coder:
 2     """
 3     @property和@私有成員名.setter
 4     """
 5     def __init__(self, name, age):
 6         self.name = name
 7         self.__age = age  #私有成員
 8 
 9     @property
10     def age(self):
11         return self.__age
12 
13     @age.setter
14     def age(self, value):
15         if 0 <= value <= 100:  # 檢驗數據的有效性,限制隨意更改屬性值
16             self.__age = value
17         else:
18             raise ValueError
19 
20 c=Coder("lennie",26)
21 # print(c.__age)  #依然不能直接調用,AttributeError
22 #1.可直接由_類名__屬性名調用
23 c._Coder__age=27
24 print(c._Coder__age)  #27
25 #2.避免使用兩個方法的繁瑣,使私有成員如變量般調用
26 c.age=30
27 # c.age=120   #ValueError
28 print(c.age)  #30

(3)設計角度講

  1. 定義:

(1) 分而治之

將一個大的需求分解爲許多類,每一個類處理一個獨立的功能。 設計角度看類的建立應先考慮方法(行爲,幹什麼),在考慮初始化(數據)

(2) 變則疏之  #拆分的度

變化的地方獨立封裝,避免影響其餘類。

(3) 高 內 聚

類中各個方法都在完成一項任務(單一職責的類)

(4) 低 耦 合    #低不是杜絕,是有互相調用才能完成

類與類的關聯性與依賴度要低(每一個類獨立),讓一個類的改變,盡少影響其餘類。

2.優點:

便於分工,便於複用,可擴展性強。

2.繼承

(1)語法角度講

  • 繼承方法
  1. 語法:

class 父類:

def 父類方法(self):

    方法體

class 子類(父類)

def 子類方法(self):

方法體

兒子 = 子類()

兒子.子類方法()

兒子.父類方法()

2.說明:子類直接擁有父類的方法.

  • 內置函數
  1. isinstance(對象, 類型返回bool值,判斷對象是不是類的對象。
  2. issubclass(類型1,類型2)返回bool值,判斷類型1是不是類型2子類。
 1 """
 2     繼承 -- 方法
 3         編程:代碼不用子類寫,可是能夠用.
 4 """
 5 class Person:
 6     def say(self):
 7         print("說話")
 8 
 9 class Student(Person):
10     def study(self):
11         print("學習")
12 
13 class Teacher(Person):
14     def teach(self):
15         print("講課")
16 
17 # 建立父類型對象,只能訪問父類型成員
18 p01 = Person()
19 p01.say()
20 
21 # 建立子類型對象,能訪問父類型成員,還能訪自身成員
22 s01 = Student()
23 s01.say()
24 s01.study()
25 
26 # "是否是 實例"
27 # s01的對象 是一種 Student類型
28 print(isinstance(s01,Student))# True
29 print(isinstance(s01,Person))# True    isinstance用於鑑定對象的直接模板以及其父模板
30 print(isinstance(s01,Teacher))# False
31 
32 # s01的類型 等於 Student類型
33 print(type(s01) == Student)# True
34 print(type(s01) == Person)# False  type只能斷定對象的直接模板
35 
36 # Student 類型 是一種 Person類型
37 print(issubclass(Student,Person))# True
38 print(issubclass(Student,Student))# True
39 print(issubclass(Student,Teacher))# False   issubclass用於鑑定兩個類是否有親屬關係
  • 繼承數據
  1. 語法

class 子類(父類):

def __init__(self,參數列表):

super().__init__(參數列表)

self.自身實例變量 = 參數

2.說明

子類若是沒有構造函數,將自動執行父類的,但若是有構造函數將覆蓋父類的,此時必須經過super()函數調用父類的構造函數,以確保父類實例變量被正常建立。

  • 定義

重用現有類的功能,並在此基礎上進行擴展。子類直接具備父類的成員(共性),還能夠擴展新功能

 1 """
 2     繼承 -- 數據
 3 """
 4 class Person:
 5     def __init__(self, name=""):
 6         self.name = name
 7 
 8 class Student(Person):
 9     def __init__(self, name="", score=0):
10         # self.name = name
11         # 若是子類沒有構造函數,使用父類構造函數
12         # 若是子類有構造函數,必須銅經過super()調用父類構造函數,
13         # 不然會覆蓋父類(不執行)的.
14         super().__init__(name)
15         self.score = score
16 
17 
18 # 建立實例變量
19 s01 = Student("lennie", 100)
20 print(s01.name)  #"lennie"
21 print(s01.score)  #100
22 p01 = Person("ginger")
23 print(p01.name)  #"ginger"
24 print(p01.score) #報錯AttributeError,子直接繼承父,但父不能調用子類成員
  • 優勢

一種代碼複用的方式。

  • 缺點

耦合度高:父類的變化,直接影響子類

(2)設計角度講

  • 定義

將相關類的共性進行抽象,統一律念,隔離變化。類是對象的抽象,同理父類是子類的抽象。

  • 適用性

多個類在概念上是一致的,且須要進行統一的處理。

 

 1 """
 2     繼承 -- 設計角度。隔離變化
 3         老張開車去東北.
 4         需求變化:還可能坐飛機,坐火車......
 5 """
 6 
 7 
 8 class Person:
 9     def __init__(self, name=""):
10         self.name = name
11 
12     def go_to(self,str_position,vehicle):
13         #經過一個參數vehicle將對象傳遞進來,從而調用方法transport(),
        鴨子類型的概念(此處傳入的對象只要有transport()方法,就視爲也是Vehicle類型,並不是必定是其子類)
14 print(self.name,vehicle.transport(),"",str_position,sep="") 15 16 17 class Vehicle: 18 """ 19 交通工具類,抽象的 20 將共有的功能(運輸)進行提取,供其子類對象繼承複用 21 """ 22 def transport(self): 23 pass 24 25 # -------------------------------------隔離變化 26 class Car(Vehicle): 27 28 def transport(self): 29 return "坐車" 30 31 class Airplane(Vehicle): 32 33 def transport(self): 34 return "坐飛機" 35 36 37 c01 = Car() 38 a01 = Airplane() 39 lz = Person("老張") 40 lz.go_to("東北",c01) #老張坐車去東北 41 lz.go_to("東北",a01) #老張坐飛機去東北

 

 

 

  • 相關概念
  1. 父類(基類、超類)、子類(派生類)。
  2. 父類相對於子類更抽象,範圍更寬泛;子類相對於父類更具體(功能更強大),範圍更狹小。
  3. 單繼承:父類只有一個(例如 JavaC#)。
  4. 多繼承:父類有多個(例如C++Python)。
  5. Object類:任何類都直接或間接繼承自 object 類。
  • 多繼承

一個子類繼承兩個或兩個以上的基類,父類中的屬性和方法同時被子類繼承下來。

MRO(Method Resolution Order)查看同名方法的解析順序:print(D.mro)類自身 --> 父類繼承列表(由左至右)--> 再上層父類

      A

     /   \

    B     C

     \   /

      D

 1 class A:
 2     pass
 3 class B(A):
 4     pass
 5 class C(A):
 6     pass
 7 class D(B,C):
 8     pass
 9 
10 print(D.mro())#[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
11 print(C.mro())#[<class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
12 print(B.mro())#[<class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
13 print(A.mro())#[<class '__main__.A'>, <class 'object'>]

3.多態

(1)設計角度講

  • 定義

父類的同一種動做或者行爲,在不一樣的子類上有不一樣的實現

  • 做用
  1. 在繼承的基礎上,體現類型的個性化(一個行爲有不一樣的實現)。
  2. 加強程序擴展性,體現開閉原則。

(2)語法角度講

  • 重寫

子類中定義(def)了父類中同名的方法(方法名、參數)。雖然該同名方法在子類中定義了,依然認爲是父類的方法。

在調用該方法時,實際執行的是子類的方法。

  • 快捷鍵

Ctrl + O

  • 內置可重寫函數

Python中,以雙下劃線開頭、雙下劃線結尾的是系統定義的成員。咱們能夠在自定義類中進行重寫,從而改變其行爲。

  • 轉換字符串

__str__函數:將對象(打印時顯示內存地址)轉換爲字符串(打印對象時顯示自定義字符串)(對人友好的)

__repr__函數:將對象轉換爲字符串(解釋器可識別的)

 

 1 """
 2     內置可重寫函數
 3         自定義對象 -->  str
 4 """
 5 class Car:
 6     def __init__(self, brand="", price=0,max_speed = 0):
 7         self.brand = brand
 8         self.price = price
 9         self.max_speed = max_speed
10 
11     # # 對人友好的,爲所欲爲的規定字符串內容-->將<__main__.Car object at 0x...>轉換爲自定義的字符串
12     # def __str__(self):
13     #     return "品牌是%s,單價是%d"%(self.brand,self.price)
14 
15     # 對解釋器友好,根據python語法規定字符串內容-->功能同__str__(),可是更強大,一般與eval("")配合使用
16     def __repr__(self):
17         return "Car('%s',%d,%d)"%(self.brand,self.price,self.max_speed)
18 
19 # 應用:將對象換成自定義的字符串顯示出來
20 c01 = Car("寶馬",1000000,260)
21 print(c01)  #"品牌是寶馬,單價是1000000",會自動調用c01.__str__()方法
22 #當已經定義了__str__()時必須顯式調用c01.__repr__()或者repr(c01)纔會執行
23 print(c01.__repr__())  #"Car('寶馬',1000000,260)"

 

拓展:eval("代碼")函數

  1. 功能:將字符串中的內容(去掉引號)直接做爲代碼運行
  2. 參數:python代碼格式的字符串,不然報錯
  3. 返回值:字符串內容代碼的執行結果
>>> eval("1+2")
3
>>> eval("lennie")          #等價於在命令行直接輸入lennie
NameError: name 'lennie' is not defined
>>> lennie
NameError: name 'lennie' is not defined
>>>

4.應用:eval("")函數與__repr__()方法配合使用,實現深拷貝對象

 

 1 # 定義技能類(技能名稱,攻擊比例,持續時間)
 2 # 建立技能對象,直接print對象.
 3 # 克隆(深拷貝)技能對象,體會改變其中一個,不影響另一個.
 4 
 5 class Skill:
 6     def __init__(self, name="", atk_ratio=0.1, duration=0.1):
 7         self.name = name
 8         self.atk_ratio = atk_ratio
 9         self.duration = duration
10 
11     def __str__(self):
12         return "%s---%d---%d" % (self.name, self.atk_ratio, self.duration)
13  
14     def __repr__(self):
15         return "Skill('%s',%d,%d)" % (self.name, self.atk_ratio, self.duration)
16 
17 
18 s01 = Skill("佛怒火蓮", 3, 5)
19 print(s01)     #佛怒火蓮---3---5
20 
21 # s02 = eval("Skill('佛怒火蓮',3,5)")
22 s02 = eval(s01.__repr__())
23 s01.name = "焰分噬浪尺"
24 print(s02)     #佛怒火蓮---3---5

 

  • 運算符重載

定義:讓自定義的類生成的對象(實例),可以像str、int、list類型數據對象同樣,使用運算符進行操做。

    • 算數運算符

    • 反向算數運算符重載

    • 複合運算符重載(不產生新對象)

    • 比較運算重載

 1 """
 2     運算符重載(重寫)
 3 """
 4 
 5 # 多態:調用父,執行子.
 6 class Vector:
 7     """
 8         向量
 9     """
10 
11     def __init__(self, x=0):
12         self.x = x
13 
14     def __str__(self):
15         return "份量是:" + str(self.x)
16 
17     # 算數運算符 --> 返回新結果
18     def __add__(self, other):
19         """
20              當前對象與其餘數據相加時被自動調用
21         :param other: 其餘數據
22         :return: 向量
23         """
24         # 若是other 是 向量
25         if type(other) == Vector:
26             return Vector(self.x + other.x)
27         # 不然
28         else:
29             return Vector(self.x + other)
30 
31     def __rsub__(self, other):
32         return Vector(other - self.x)
33 
34     # 複合運算符 --> 在原有對象基礎上修改
35     def __iadd__(self, other):
36         self.x += other
37         return self
38 
39     def __gt__(self, other):
40         return self.x  > other.x
41 
42 # 1. 算數運算符
43 v01 = Vector(10)
44 # 自定義向量 + 整數  -->  向量
45 print(v01 + 5)  # v01.__add__(5)
46 # 自定義向量 + 自定義向量  -->  向量
47 v02 = Vector(2)
48 print(v01 + v02)
49 
50 # 2. 反向算數運算符
51 # 整數 - 自定義向量 --> 向量
52 print(5 - v01)  # v01.__rsub__(5)
53 
54 """
55 list01 = [1]
56 
57 # print(id(list01))
58 list01 += [2]# 在原有基礎上增長新元素
59 # print(id(list01))
60 
61 # print(id(list01))
62 list01 = list01 + [3]# 產生新的對象
63 # print(id(list01))
64 
65 print(list01)
66 """
67 
68 # 3. 複合運算符
69 #       默認使用算數運算符
70 print(id(v02))
71 v02 += 3  # v02 = v02 + 3
72 print(v02)
73 print(id(v02))
74 
75 # 4. 比較運算符
76 # 自定義類 > 自定義類
77 print(v01 > v02)# v01.__gt__(v02)

4、六大設計原則

1.開-閉原則(目標、總的指導思想)

Open Closed Principle

對擴展開放,對修改關閉。

增長新功能,不改變原有代碼。

2.類的單一職責(一個類的定義)

Single Responsibility Principle   

一個類有且只有一個改變它的緣由。

3.依賴倒置(依賴抽象)

Dependency Inversion Principle

客戶端代碼(調用的類)儘可能依賴(使用)抽象。

抽象不該該依賴細節,細節應該依賴抽象。

4.組合複用原則(複用的最佳實踐)

Composite Reuse Principle

若是僅僅爲了代碼複用優先選擇組合複用,而非繼承複用。

組合的耦合性相對繼承低。

5.里氏替換(繼承後的重寫,指導繼承的設計)

Liskov Substitution Principle

父類出現的地方能夠被子類替換,在替換後依然保持原功能。

子類要擁有父類的全部功能。

子類在重寫父類方法時,儘可能選擇擴展重寫,防止改變了功能。在Ctrl+o時,別刪東西,重寫後擴展重寫

6.迪米特法則(類與類交互的原則)

Law of Demeter

不要和陌生人說話。

類與類交互時,在知足功能要求的基礎上,傳遞的數據量越少越好。由於這樣可能下降耦合度(利用抽象去調用子類,隔離變化)。

 1 """
 2     面向對象的應用:
 3         定義員工管理器,記錄全部員工,計算總工資.
 4         程序員:底薪 + 項目分成
 5         測試員:底薪 + bug * 5
 6         要求:增長新的崗位,不影響員工管理器.
 7         指出:三大特徵,六個原則.
 8 
 9         封裝:員工管理器,程序員,測試員
10         繼承:員工隔離員工管理器與具體員工的變化
11         多態:員工管理器調用員工,執行程序員,測試員.
12         開閉:增長新的崗位,不影響員工管理器.
13         單一:員工管理器(管理員工),員工(隔離變化),程序員(計算薪資),測試員(計算薪資)
14         依賴倒置:員工管理器調用員工,而不調用程序員/測試員
15         組合複用:員工管理器存儲具體員工的變量
       里氏替換:程序員(繼承員工底薪後添加獎金),測試員(繼承員工底薪後添加bug提成)
       迪米特法則:員工管理器與程序員,測試員的交互,改由與員工交互,減小了數據的傳遞
16 """ 17 18 19 class EmployeeManager: 20 """ 21 員工管理器 22 """ 23 24 def __init__(self): 25 self.__all_employee = [] 26 27 def add_employee(self, emp): 28 # 判斷 emp 是員工 則 添加....? 29 # if type(emp) == Employee: 30 # emp 屬於 員工 31 if isinstance(emp, Employee): 32 self.__all_employee.append(emp) 33 34 def get_total_salary(self): 35 total_sarlay = 0 36 for itme in self.__all_employee: 37 # 調用的是員工 38 # 執行的是程序員/測試員 39 total_sarlay += itme.calculate_salary() 40 return total_sarlay 41 42 43 class Employee: 44 """ 45 員工 46 抽象的 --> 統一律念 --> 隔離變化 47 48 """ 49 50 def __init__(self, base_salary): 51 self.base_salary = base_salary 52 53 def calculate_salary(self): 54 """ 55 計算薪資 56 :return: 小數類型的薪資 57 """ 58 return self.base_salary 59 60 61 class Programmer(Employee): 62 """ 63 程序員 64 """ 65 66 def __init__(self, base_salary=0, bonus=0): 67 super().__init__(base_salary) 68 self.bonus = bonus 69 70 def calculate_salary(self): 71 # return self.base_salary + self.bonus 72 return super().calculate_salary() + self.bonus 73 74 75 class Tester(Employee): 76 def __init__(self, base_salary=0, bug_count=0): 77 super().__init__(base_salary) 78 self.bug_count = bug_count 79 80 def calculate_salary(self): 81 return super().calculate_salary() * self.bug_count * 5 82 83 # def calculate_salary(self): 84 # return self.base_salary * self.bug_count * 5 85 86 manager = EmployeeManager() 87 p01 = Programmer(32000, 50000) 88 manager.add_employee(p01) 89 manager.add_employee(Tester(8000, 2)) 90 print(manager.get_total_salary())
相關文章
相關標籤/搜索