Python之路系列:面向對象初級:靜態屬性、靜態方法、類方法

1、靜態屬性

靜態屬性至關於數據屬性。html

用@property語法糖裝飾器將類的函數屬性變成能夠不用加括號直接的相似數據屬性。python

能夠封裝邏輯,讓用戶感受是在調用一個普通的數據屬性。linux

例子:算法

 

class Room: def __init__(self,name,length,width,heigh): self.name = name self.length = length self.width = width self.heigh = heigh ​ @property #經過使用property語法糖裝飾器,使類的函數屬性返回相似數據屬性,不用使用括號。
    def cacl_area(self): return self.length*self.width ​ ​ r1 = Room("nick",20,30,10) print(r1.cacl_area)

 


 


 

 

2、類方法

需求:類不經過實例(對象)直接調用類的函數屬性。網絡

類沒法直接調用本身的函數屬性,須要藉助實例對象。ide

例子:函數

class Room: def __init__(self,name,length,width,heigh): self.name = name self.length = length self.width = width self.heigh = heigh ​ def cacl_area(self): return self.length*self.width ​ def tell_info(self): print("-----") ​ Room.tell_info() #這樣直接用類調用函數屬性會出錯,須要實例對象
r1=Room("nick",10,10,10) r1.tell_info()

須要用@classmethod ,將函數作成類方法,執行類方法時,自動將調用該方法的複製給cls工具

例子:spa

class Room: def __init__(self,name,length,width,heigh): self.name = name self.length = length self.width = width self.heigh = heigh ​ def cacl_area(self): return self.length*self.width ​ @classmethod def tell_info(cls,msg): print(cls) print("-----",msg) ​ Room.tell_info("hello")  # 至關於Room.tell_info(Room,"hello"),classmethod自動將類傳入了

 


 


 

 

3、靜態方法

用@staticmethod 語法糖實現,不和具體的實例對象進行綁定,類能夠調用實例也能夠調用設計

例子:

class Room: def __init__(self,name,length,width,heigh): self.name = name self.length = length self.width = width self.heigh = heigh ​ def cacl_area(self): return self.length*self.width ​ @staticmethod def test(a,b,c): print(a,b,c) ​ ​ Room.test("hello","world","!!!")  #靜態方法類能夠調用,實例也能夠調用
r1 = Room("nick",20,30,10) r1.test("nick","hello","!!!")

 

 

輸出結果:

hello world !!!
nick hello !!!

 

靜態方法只是名義上歸屬類管理,不能使用類變量和實例變量,是類的工具包。

補充:

綁定方法說明:

  ♦綁定方法(綁定給誰,誰來調用就自動將它自己看成第一個參數傳入)

  ♦綁定到對象的方法:沒有被任何裝飾器裝飾的方法。

  ♦綁定給類的方法(classmethod)

  ♦非綁定方法:用staticmethod裝飾器裝飾的方法:不與類或對象綁定,類和對象均可以調用,可是沒有自動傳值。


 


 

 

4、組合

組合指的是,在一個類中以另一個類的對象做爲數據屬性,稱爲類的組合

用途:

  一、不一樣的類作關聯

  二、不一樣的類組合成大的總體。

例子1:

class School: def __init__(self,name,addr,type): self.name = name self.addr = addr self.type = type ​ ​ def enrol_students(self): print("%s正在招生"%self.name) ​ ​ def exam(self): print("%s正在考試"%self.name) ​ ​ class Course: ​ ​ def __init__(self,name,price,period,school): self.name = name self.price = price self.period = period self.school = school ​ ​ s1 = School("清華","北京","公立") s2 = School("北大","北京","公立") ​ school_dic = { "1":s1, "2":s2 } ​ msg = """ 請選擇學校 一、清華 二、北大 ​ """
while True: print(msg) school_choice = input(">>>: ").strip() name = input("課程名:").strip() price = input("課程費用:").strip() period = input("課程週期:").strip() school_obj = school_dic[school_choice] c1 = Course(name,price,period,school_obj)  #直接傳入學校的對象,不一樣類之間創建了關聯
    print("%s屬於%s"%(c1.name,c1.school.name))

 

 

例子2:

class Head(): pass ​ ​ class Trunk: pass ​ ​ class Hand(): pass ​ ​ class Foot(): pass ​ ​ class People(): def __init__(self, name, age, gerder, ): self.name = name self.age = age self.gerder = gerder self.head = Head()  # 人的頭繼承了head類這個類得實例,這裏是一個對象
        self.trunk = Trunk() self.hand = Hand() self.foot = Foot() ​ ​ p1 = People("nick", '18', "man") print(p1.__dict__)  # 打印獲得一個屬性head ->>> key的值對應的head實例

 

例子3:

rom math import pi ​ class Circle: def __init__(self,radius): self.radius = radius ​ ​ def cacl_area(self): print("圓的面積是%s"%(pi*self.radius*self.radius)) return pi*self.radius*self.radius ​ ​ class Ring: def __init__(self,outside_radius,inside_radius): self.outside_circle = Circle(outside_radius)  #獲取外圈圓的對象
        self.inside_circle = Circle(inside_radius)    #獲取內圈圓的對象
​ ​ def area(self): ring_area = self.outside_circle.cacl_area() - self.inside_circle.cacl_area() print("圓環的面積是%s"%ring_area) ​ ​ r1 = Ring(10,8) r1.area()

 

 

5、繼承

什麼是繼承?


 

  繼承指的是類與類之間的關係,是一種什麼「是」什麼的關係,繼承是一種建立新類的方式,新建的類能夠繼承一個或多個父類(python支持多繼承),父類又可稱爲基類或超類,新建的類稱爲派生類或子類。

子類會「」遺傳」父類的屬性,從而解決代碼重用問題。

例子1:

class Parent_class: money = 10
    def __init__(self,name): self.name = name ​ def make_money(self): print("make good money") ​ ​ class Sub_class(Parent_class): pass ​ s1 = Sub_class("nick")  #這裏須要傳入一個參數,雖然子類沒有默認的__init__方法,子類找不到去父類找,父類須要一個參數
print(s1.money) #這裏直接調用父類的數據屬性
s1.make_money() #調用父類的函數屬性

 

 

分析:子類繼承了父類的數據屬性和函數屬性

 

派生


 

  子類也能夠添加本身新的屬性或者在本身這裏從新定義這些屬性(不會影響到父類),須要注意的是,一旦從新定義了本身的屬性且與父類重名,那麼調用新增的屬性時,就以本身爲準了。

 

例子2:

class Parent_class: money = 10
    def __init__(self,name): self.name = name ​ def make_money(self): print("make good money") ​ ​ class Sub_class(Parent_class): money =  10000 ​ s1 = Sub_class("nick") print(s1.money) #這裏子類有本身的與父類同名的屬性,從子類的屬性開始找,本身有就用本身的數據,再也不用父類的數據
print(Parent_class.money) #父類的屬性任然存在,而不是被子類的同名屬性覆蓋了
s1.make_money() #調用父類的函數屬性

 

分析:子類也能夠添加本身新的屬性或者在本身這裏從新定義這些屬性(不會影響到父類),須要注意的是,一旦從新定義了本身的屬性且與父類重名,那麼調用新增的屬性時,就以本身爲準了。父類的屬性任然存在。

 

在子類中,新建的重名的函數屬性,在編輯函數內功能的時候,有可能須要重用父類中重名的那個函數功能,應該是用調用普通函數的方式,即:類名.func(),此時就與調用普通函數無異了,所以即使是self參數也要爲其傳值。

class Parent_class1: money = 10
    def __init__(self,name): self.name = name ​ def make_money(self): print("from Parent_class1") print("make good money") ​ ​ class Sub_class(Parent_class1,): money =  10000def make_money(self):  #在本身這裏定義新的make_money,再也不使用父類的make_money,且不會影響父類
        Parent_class1.make_money(self)   #若是想調用父類的同名函數屬性,這裏要本身手寫self。
        print("from Sub_class") ​ ​ s1 = Sub_class("nick") s1.make_money()

 

屬性查找


 

屬性引用查找,會先從對象中找,而後去類中找,而後再去父類中找...直到最頂級的父類。

例子:

class Parent_class1: def f1(self): print("from Parent_class f1") ​ def f2(self): self.f1() print("from Parent_class f2") ​ ​ class Sub_class(Parent_class1,): def f1(self): print("from Sub_class") ​ ​ s1 = Sub_class() s1.f2()

 

輸出結果:

from Sub_class from Parent_class f2

 

 

分析:對象s1本身的類自己沒有f2()函數屬性,到父類找,找到了執行self.f1(),這裏的self是對象s1,因此s1.f1()函數屬性執行結果是輸出from Sub_class,而後執行print("from Parent_class f2")輸出from Parent_class f2

 

查看繼承


 

__base__、__bases__方法

class Parent_class1: money = 10
    def __init__(self,name): self.name = name ​ def make_money(self): print("make good money") ​ class Parent_class2: passclass Sub_class(Parent_class1,Parent_class2): money =  10000 ​ ​ print(Sub_class.__base__) print(Sub_class.__bases__)

 

輸出結果:

<class '__main__.Parent_class1'> (<class '__main__.Parent_class1'>, <class '__main__.Parent_class2'>)

 

分析:

  
#__base__只查看從左到右繼承的第一個子類,__bases__則是查看全部繼承的父類

何時用繼承?


 

  • 當類之間有不少相同的功能,提取這些共同的功能作成基類,用繼承比較好。

    經過繼承創建了派生類與基類之間的關係,它是一種'是'的關係,好比白馬是馬,人是動物。

    好比老師是人,學生是人。

  • 當類之間有顯著不一樣,而且較小的類是較大的類所須要的組件時,用組合比較好。

    用組合的方式創建了類與組合的類之間的關係,它是一種‘有’的關係,好比教授有生日,教授教python和linux課程,教授有學生s一、s二、s3。


     


     

6、接口繼承和統一化設計

 

繼承同時具備兩種含義:

  • 繼承基類的方法,而且作出本身的改變或者擴展(解決代碼重用問題)。

  • 聲明某個子類兼容於某基類,定義一個接口類,子類繼承接口類,而且實現接口中定義的方法。

繼承的第二種含義很是重要。它又叫「接口繼承」。

封裝:封裝的意義,在於明確標識出容許外部使用的全部成員函數和數據項,或者叫接口。

真正的封裝是,通過深刻的思考,作出良好的抽象,給出「完整且最小」的接口,並使得內部細節能夠對外透明(注意:對外透明的意思是,外部調用者能夠順利的獲得本身想要的任何功能,徹底意識不到內部細節的存在

 

總結


 

 (1)實踐中繼承的第一種含義意義並非很大,甚至經常是有害的。由於它使得子類與基類出現強耦合。

 (2)繼承的第二種含義很是重要。接口繼承實質上是要求「作出一個良好的抽象,這個抽象規定了一個兼容接口,使得外部調用者無需關心具體細節,可一視同仁的處理實現了特定接口的全部對象」,這在程序設計上叫作歸一化。

歸一化使得高層的外部使用者能夠不加區分的處理全部接口兼容的對象集合——就好象linux的泛文件概念同樣,全部東西均可以當文件處理,沒必要關心它是內存、磁盤、網絡仍是屏幕。

​ 接口提取了一羣類共同的函數,能夠把接口當作一個函數的集合。而後讓子類去實現接口中的函數。歸一化,就是隻要是基於同一個接口實現的類,那麼全部的這些類產生的對象在使用時,從用法上來講都同樣。歸一化,讓使用者無需關心對象的類是什麼,只須要的知道這些對象都具有某些功能就能夠了,這極大地下降了使用者的使用難度。

 

抽象類


 

抽象類是一個特殊的類,它的特殊之處在於只能被繼承,不能被實例化。

若是說類是從一堆對象中抽取相同的內容而來的,那麼抽象類就是從一堆類中抽取相同的內容而來的,內容包括數據屬性和函數屬性。

  好比咱們有香蕉的類,有蘋果的類,有桃子的類,從這些類抽取相同的內容就是水果這個抽象的類,你吃水果時,要麼是吃一個具體的香蕉,要麼是吃一個具體的桃子。。。。。。你永遠沒法吃到一個叫作水果的東西。

​ 從設計角度去看,若是類是從現實對象抽象而來的,那麼抽象類就是基於類抽象而來的。

  從實現角度來看,抽象類與普通類的不一樣之處在於:抽象類中只能有抽象方法(沒有實現功能),該類不能被實例化,只能被繼承,且子類必須實現抽象方法。

 

抽象類與接口


 

抽象類的本質仍是類,指的是一組類的類似性,包括數據屬性(如all_type)和函數屬性(如read、write),而接口只強調函數屬性的類似性。

*抽象類是一個介於類和接口直接的一個概念,同時具有類和接口的部分特性,能夠用來實現歸一化設計*

 

接口繼承的使用例子(抽象類)


 

import abc class All_file(metaclass=abc.ABCMeta):   # 接口類(基類),接口類的方法不須要具體實現,也不須要實例化
​ @abc.abstractmethod #引入第三方模塊abc,調用abc下的abstractmethod,強制子類下必須有read方法
    def read(self): pass ​ @abc.abstractmethod #強制子類下必須有write方法
    def write(self): pass ​ ​ class Text_file(All_file): def read(self): print("from text-file read") ​ def write(self): print("from text-file write") ​ ​ class Disk(All_file): def read(self): print("from disk read") ​ def write(self): print("from disk write") ​ class Cdrom(All_file): def read(self): print("from cdrom read") ​ def write(self): print("from cdrom write") ​ f1 = Disk() f1.read()

 


 


 

 

7、繼承順序

經典類與新式類


 

在python2中,新式類基類繼承object類,經典類不繼承任何類

\

  默認都是經典類,只有顯式繼承了object纔是新式類,即:

class Person(object):pass 新式類寫法

class Person():pass 經典類寫法

class Person:pass 經典類寫

 

在Python 3.x中取消了經典類,默認都是新式類,而且沒必要顯式的繼承object

  
class Person(object):pass
class Person():pass
class Person:pass
三種寫法並沒有區別

 

繼承順序


 

Python中子類能夠同時繼承多個父類,如A(B,C,D)

若是繼承關係爲非菱形結構,則會按照先找B這一條分支,而後再找C這一條分支,最後找D這一條分支的順序直到找到咱們想要的屬性。

深度優先和廣度優先


 

繼承關係爲菱形結構,那麼屬性的查找方式有兩種,分別是:深度優先和廣度優先。

 

 例子:

class G: def test(self): print("from G") ​ class F(G): def test(self): print("from F") ​ class E(G): def test(self): print("from E") ​ ​ class D(G): def test(self): print("from D") ​ class C(F): def test(self): print("from C") ​ class B(E): def test(self): print("from B") passclass A(B,C,D): def test(self): print("from A") # pass
​ c1 = A() c1.test() ​ # python3環境中,新式類繼承的尋找的順序是A-B-E-C-F-D-G #在python2環境中,這個爲經典類,繼承尋找的順序爲A-B-E-G-C-F-D #python3中統一都是新式類,所有按照廣度優先繼承 #pyhon2中才分新式類與經典類

 

 

 

繼承原理


 

對於定義的每個類,python會計算出一個方法解析順序(MRO)列表,這個MRO列表就是一個簡單的全部基類的線性順序列表。

爲了實現繼承,python會在MRO列表上從左到右開始查找基類,直到找到第一個匹配這個屬性的類爲止。而這個MRO列表的構造是經過一個C3線性化算法來實現的。

C3線性化算法實際上就是合併全部父類的MRO列表並遵循以下三條準則:

  • 子類會先於父類被檢查

  • 多個父類會根據它們在列表中的順序被檢查

  • 若是對下一個類存在兩個合法的選擇,選擇第一個父類

例子(python3)

class G: def test(self): print("from G") ​ class F(G): def test(self): print("from F") ​ class E(G): def test(self): print("from E") ​ class D(G): def test(self): print("from D") ​ class C(F): def test(self): print("from C") ​ class B(E): def test(self): print("from B") passclass A(B,C,D): def test(self): print("from A") ​ print(A.mro())

 

輸出結果:

[<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class '__main__.G'>, <class 'object'>]

 

分析:#python2中的新式類也和python3同樣,都有mro方法,python2中經典類沒有mro方法。


 


 

 

8、子類調用父類方法

在子類派生出的新方法中,每每須要重用父類的方法,咱們有兩種方式實現

第一種方式:直接調用


 

指名道姓,即父類名.父類方法()

class Vehicle:  #定義父類
def __init__(self,name,speed,power): self.name = name self.speed = speed self.power = power ​ def run(self): print("running !") ​ class Subway(Vehicle):  #定義子類
def __init__(self,name,speed,power,line): Vehicle.__init__(self,name,speed,power)  #這裏調用父類的構造方法,直接用父類名.構造方法名(),
                                                 # 須要本身傳入self和其餘父類的參數
        self.line = line ​ def run(self): Vehicle.run(self) #這裏調用父類的run()方法也是採用父類名.父類方法()的方式,也是須要本身傳入self
        print("%s %s號線開動了"%(self.name,self.line)) ​ s = Subway("深圳地鐵","70km/h","電力",1) s.run()

 

第二種方式:super()


 

用super().父類方法(),這種方式不用寫父類的名字,不用再傳入self這個參數了。

 

class Vehicle:  #定義父類
def __init__(self,name,speed,power): self.name = name self.speed = speed self.power = power ​ def run(self): print("running !") ​ class Subway(Vehicle):  #定義子類
def __init__(self,name,speed,power,line): super().__init__(name,speed,power)  #這裏用super().父類的方法名調用父類的方法,這種方式不用傳入self參數
        self.line = line ​ def run(self): super().run() #這裏調用父類的run()方法也是採用super().父類方法()的方式,不須要本身傳入self參數
        print("%s %s號線開動了"%(self.name,self.line)) ​ s = Subway("深圳地鐵","70km/h","電力",1) s.run()

 轉自連接

相關文章
相關標籤/搜索