1. 程式人生 > >Python學習—面向對象學習上

Python學習—面向對象學習上

多繼承 進行 trac sin __init__ 去掉 python2 面向 例子

面向對象簡介

OOP編程是利用“類”和“對象”來創建各種模型來實現對真實世界的描述,使用面向對象編程的原因一方面是因為它可以使程序的維護和擴展變得更簡單,並且可以大大提高程序開發效率 ,另外,基於面向對象的程序可以使它人更加容易理解你的代碼邏輯,從而使團隊開發變得更從容。

面向對象的三大特性

Encapsulation 封裝
把客觀事物封裝成抽象的類,並且類可以把自己的屬性和方法只讓可信的類或者對象操作,對不可信的進行信息隱藏。
註意的是。這裏說的屬性並不僅僅是基本數據類型,還包括引用數據類型,也就是說,我們同樣可以在一個類中封裝其他類的對象,使其在這個類中實現引用類的相應辦法。

Inheritance 繼承


首先,什麽叫繼承呢?從理解上來說就是兒子獲得了父親所有的東西,並且這些東西是屬於兒子的,兒子可以隨意支配。那麽從編程語言角度出發,就是一個類獲取了另外一個類的全部方法,並且對這些方法進行自主的支配。在這裏,被別人繼承的類,我們叫父類,也叫超類或者基類。而繼承了父類的類呢,就叫子類,也叫派生類。
所以一個類可以派生出子類,而子類會自動繼承在這個父類裏定義的屬性、方法

Polymorphism 多態
多態的定義:指允許不同類的對象對同一消息做出響應。即同一消息可以根據發送對象的不同而采用多種不同的行為方式。(發送消息就是函數調用)

實現多態的技術稱為:動態綁定(dynamic binding),是指在執行期間判斷所引用對象的實際類型,根據其實際的類型調用其相應的方法。

多態是面向對象的重要特性,簡單點說:“一個接口,多種實現”,指一個基類中派生出了不同的子類,且每個子類在繼承了同樣的方法名的同時又對父類的方法做了不同的實現,這就是同一種事物表現出的多種形態。

現實中,關於多態的例子不勝枚舉。比方說按下 F1 鍵這個動作,如果當前在 Flash 界面下彈出的就是 AS 3 的幫助文檔;如果當前在 Word 下彈出的就是 Word 幫助;在 Windows 下彈出的就是 Windows 幫助和支持。同一個事件發生在不同的對象上會產生不同的結果。就好像糖一樣,有多種口味,你想吃什麽口味的就可以吃什麽口味。但在程序中,卻不是你想要怎樣就怎樣。更多的是需要怎樣去做就怎樣去做。來一個算是比較官方的解釋:在面向對象語言中,接口的多種不同的實現方式即為多態。

編程其實就是一個將具體世界進行抽象化的過程,多態就是抽象化的一種體現,把一系列具體事物的共同點抽象出來, 再通過這個抽象的事物, 與不同的具體事物進行對話。
對不同類的對象發出相同的消息將會有不同的行為。比如,你的老板讓所有員工在九點鐘開始工作, 他只要在九點鐘的時候說:“開始工作”即可,而不需要對銷售人員說:“開始銷售工作”,對技術人員說:“開始技術工作”, 因為“員工”是一個抽象的事物, 只要是員工就可以開始工作,他知道這一點就行了。至於每個員工,當然會各司其職,做各自的工作。
多態允許將子類的對象當作父類的對象使用,某父類型的引用指向其子類型的對象,調用的方法是該子類型的方法。這裏引用和調用方法的代碼編譯前就已經決定了,而引用所指向的對象可以在運行期間動態綁定

封裝

1.類和對象

Class 類
一個類即是對一類擁有相同屬性的對象的抽象、藍圖、原型。在類中定義了這些對象的都具備的屬性(variables(data))、共同的方法。
對事物抽象出來類,即是封裝。

Object 對象
一個對象即是一個類的實例化後的實例,即對象是類的實例。一個類必須經過實例化後方可在程序中調用,一個類可以實例化多個對象,每個對象亦可以有不同的屬性,就像人類是指所有人,每個人是指具體的對象,人與人之前有共性,亦有不同

python中使用class保留字來定義類,類名的首字母一定要大寫。

以Student類為例,在Python中,定義類是通過class關鍵字:

class Student(object):
    pass

class後面緊接著是類名,即Student,類名通常是大寫開頭的單詞,緊接著是(object),表示該類是從哪個類繼承下來的,繼承的概念我們後面再講,通常,如果沒有合適的繼承類,就使用object類,這是所有類最終都會繼承的類。

定義好了Student類,就可以根據Student類創建出Student的實例,創建實例是通過類名+()實現的:

>>> bart = Student()
>>> bart
<__main__.Student object at 0x10a67a590>
>>> Student
<class ‘__main__.Student‘>

可以看到,變量bart指向的就是一個Student的實例,後面的0x10a67a590是內存地址,每個object的地址都不一樣,而Student本身則是一個類。

可以自由地給一個實例變量綁定屬性,比如,給實例bart綁定一個name屬性:

>>> bart.name = ‘Bart Simpson‘
>>> bart.name
‘Bart Simpson‘

由於類可以起到模板的作用,因此,可以在創建實例的時候,把一些我們認為必須綁定的屬性強制填寫進去。通過定義一個特殊的__init__方法,在創建實例的時候,就把name,score等屬性綁上去:

class Student(object):

    def __init__(self, name, score):
        self.name = name
        self.score = score

註意:特殊方法“__init__”前後分別有兩個下劃線!!!

註意到__init__方法的第一個參數永遠是self,表示創建的實例本身,因此,在__init__方法內部,就可以把各種屬性綁定到self,因為self就指向創建的實例本身。

有了__init__方法,在創建實例的時候,就不能傳入空的參數了,必須傳入與__init__方法匹配的參數,但self不需要傳,Python解釋器自己會把實例變量傳進去:

>>> bart = Student(‘Bart Simpson‘, 59)
>>> bart.name
‘Bart Simpson‘
>>> bart.score
59

和普通的函數相比,在類中定義的函數只有一點不同,就是第一個參數永遠是實例變量self,並且,調用時,不用傳遞該參數。除此之外,類的方法和普通函數沒有什麽區別,所以,你仍然可以用默認參數、可變參數、關鍵字參數和命名關鍵字參數。
註意:self代表類的實例,而非類!

2.實例屬性和類屬性

屬性有兩種:實例屬性、類屬性。

實例屬性是在構造函數init中定義的,定義時以self為前綴。
類屬性是在類中方法之外定義的。
實例屬性屬於對象(實例),只能在實例化對象後通過對象名字訪問。類屬性屬於類,可直接通過類名訪問,不用實例化對象(盡管也可以通過對象來訪問類屬性,但不建議這樣做,因為這樣做會導致類屬性值不一致)。類屬性還可以在類定義之後,在程序中通過類名增加,如:類名.類屬性=值

共有屬性和私有屬性

屬性名以雙下劃線__開頭的則是私有屬性,否則是共有屬性。私有屬性在類外不能直接被訪問。但python提供了訪問私有屬性的方式,可用於程序的測試和調試。方式如下:

對象名.__類名+私有屬性名字

例如:

class People(object):
    def __init__(self,name,money):
        self.name = name
        self.__money = money

p1 = People(‘user1‘, 1000000)
p1.__People__money = 999999 #訪問私有屬性並修改~
print(p1.__People__money)

3.類的方法

類的方法:
在類的內部,使用 def 關鍵字來定義一個方法,與一般函數定義不同,類方法必須包含參數 self,且為第一個參數,self 代表的是類的實例對象。
self 的名字並不是規定不變的(因為是形參),也可以使用 this,但是最好還是按照約定是用 self。

公有方法和私有方法

公有方法、私有方法都屬於對象,每個對象都有自己的公有方法、私有方法。公有方法通過對象名調用,私有方法則不能通過對象名調用,只能在屬於該對象的方法中通過self來調用,即只能在類的內部調用 ,不能在類地外部調用。
兩個下劃線開頭,聲明該方法為私有方法。公有方法直接是寫一個方法名即可。

類方法、靜態方法

1.類方法通過@classmethod裝飾器實現,類方法和普通方法的區別是, 類方法只能訪問類變量,不能訪問實例變量

class Dog(object):
    def __init__(self,name):
        self.name = name
    @classmethod
    def eat(self):
        print("%s is eating" % self.name)

d = Dog("hahaha")
d.eat()

執行報錯如下,說Dog沒有name屬性,因為name是個實例變量,類方法是不能訪問實例變量的

Traceback (most recent call last):
  File "/Users/jieli/PycharmProjects/python_projects/面向對象/類方法.py", line 9, in <module>
    d.eat()
  File "/Users/jieli/PycharmProjects/python_projects/面向對象/類方法.py", line 6, in eat
    print("%s is eating" % self.name)
AttributeError: type object ‘Dog‘ has no attribute ‘name‘

此時可以定義一個類變量,也叫name,看下執行效果

class Dog(object):
    name = "hahahahahahaha"
    def __init__(self,name):
        self.name = name
    @classmethod
    def eat(self):
        print("%s is eating" % self.name)

d = Dog("hahaha")
d.eat()

運行輸出:
hahahahahahaha is eating

2.通過@staticmethod裝飾器即可把其裝飾的方法變為一個靜態方法。普通的方法,可以在實例化後直接調用,並且在方法裏可以通過self.調用實例變量或類變量,但靜態方法是不可以訪問實例變量或類變量的,一個不能訪問實例變量和類變量的方法,其實相當於跟類本身已經沒什麽關系了,它與類唯一的關聯就是需要通過類名來調用這個方法

class Dog(object):
    def __init__(self,name):
        self.name = name
    @staticmethod #把eat方法變為靜態方法
    def eat(self):
        print("%s is eating" % self.name)

d = Dog("hahaha")
d.eat()

上面的調用會出以下錯誤,說是eat需要一個self參數,但調用時卻沒有傳遞,沒錯,當eat變成靜態方法後,再通過實例調用時就不會自動把實例本身當作一個參數傳給self了。

Traceback (most recent call last):
  File "/Users/jieli/PycharmProjects/python_projects/面向對象/靜態方法.py", line 9, in <module>
    d.eat()
TypeError: eat() missing 1 required positional argument: ‘self‘

想讓上面的代碼可以正常工作有兩種辦法

1.調用時主動傳遞實例本身給eat方法,即d.eat(d)

class Dog(object):
    def __init__(self,name):
        self.name = name
    @staticmethod #把eat方法變為靜態方法
    def eat(self):
        print("%s is eating" % self.name)

d = Dog("hahaha")
d.eat(d)

運行輸出:
hahaha is eating

2.在eat方法中去掉self參數,但這也意味著,在eat中不能通過self.調用實例中的其它變量了

class Dog(object):
    def __init__(self,name):
        self.name = name
    @staticmethod
    def eat():
        print(" is eating")

d = Dog("hahaha")
d.eat()

運行輸出:
 is eating

屬性方法  

屬性方法的作用就是通過@property把一個方法變成一個靜態屬性

class Dog(object):
    def __init__(self,name):
        self.name = name
    @property
    def eat(self):
        print(" %s is eating" %self.name)

d = Dog("hahaha")
d.eat()

調用會出以下錯誤, 說NoneType is not callable, 因為eat此時已經變成一個靜態屬性了, 不是方法了, 想調用已經不需要加()號了,直接d.eat就可以了

Traceback (most recent call last):
 ChenRonghua is eating
  File "/Users/jieli/PycharmProjects/python_projects/面向對象/屬性方法.py", line 8, in <module>
    d.eat()
TypeError: ‘NoneType‘ object is not callable

正常調用如下

d = Dog("hahaha")
d.eat

輸出
 hahaha is eating

下一篇博客會涉及@property的詳細情況,這裏只作簡介
另外可以參考博客:http://www.cnblogs.com/alex3714/articles/5213184.html

繼承

1.單繼承

顧名思義,單繼承是指子類只繼承一個父類。

#定義類
class People:
    #定義基本屬性
    name = ‘‘
    age = 0
    #定義私有屬性,私有屬性在類外部無法直接進行訪問
    __weight = 0
    #定義構造方法
    def __init__(self,n,a,w):
        self.name = n
        self.age = a
        self.__weight = w
    def speak(self):
        print("%s 說: 我 %d 歲。" %(self.name,self.age))

#單繼承
class Student(people):
    grade = ‘‘
    def __init__(self,n,a,w,g):
        #調用父類的構函
        People.__init__(self,n,a,w)
                #或者用super方法
                #super(People, self).__init__(n,a,w)
        self.grade = g
    #覆寫父類的方法
    def speak(self):
        print("%s 說: 我 %d 歲了,我在讀 %d 年級"%(self.name,self.age,self.grade))

s = Student(‘ken‘,10,60,3)
s.speak()

2.多繼承

子類同時繼承多個父類


#類定義
class People:
    #定義基本屬性
    name = ‘‘
    age = 0
    #定義私有屬性,私有屬性在類外部無法直接進行訪問
    __weight = 0
    #定義構造方法
    def __init__(self,n,a,w):
        self.name = n
        self.age = a
        self.__weight = w
    def speak(self):
        print("%s 說: 我 %d 歲。" %(self.name,self.age))

 #單繼承
 class Student(people):
    grade = ‘‘
    def __init__(self,n,a,w,g):
        #調用父類的構函
        People.__init__(self,n,a,w)
        self.grade = g
    #覆寫父類的方法
    def speak(self):
        print("%s 說: 我 %d 歲了,我在讀 %d 年級"%(self.name,self.age,self.grade))

#另一個類,多重繼承之前的準備
class Speaker():
    topic = ‘‘
    name = ‘‘
    def __init__(self,n,t):
        self.name = n
        self.topic = t
    def speak(self):
        print("我叫 %s,我是一個演說家,我演講的主題是 %s"%(self.name,self.topic))

#多繼承
class Sample(Speaker,Student):
    a =‘‘
    def __init__(self,n,a,w,g,t):
        Student.__init__(self,n,a,w,g)
        Speaker.__init__(self,n,t)

test = Sample("Tim",25,80,4,"Python")
test.speak()   #方法名同,默認調用的是在括號中排前地父類的方法

經典類和新式類

      經典類的繼承算法: 深度優先算法
      新式類的繼承算法: 廣度優先算法

python3全部都是新式類
在python2中既有新式類也有經典類

  • 在繼承後,通過子類的對象調用某方法,如果子類沒有, 則去父類找, 如果父類也沒有, 就報錯。
  • 私有屬性、私有方法, 在類的內部都是可以訪問的, 類的外部或者子類都不可以訪問

多態

Pyhon 很多語法都是支持多態的,比如 len(),sorted()等, 你給len傳字符串就返回字符串的長度,傳列表就返回列表長度。

關於多態的講解,廖雪峰老師的教程裏講的很容易理解,這裏附上地址:繼承和多態參考文章
另外附上老男孩Python3教程的面向對象講解,因為他最後講到了領域模型:傳送門

最後關於多態,截取廖雪峰老師的一點講解:
靜態語言 vs 動態語言

對於靜態語言(例如Java)來說,如果需要傳入Animal類型,則傳入的對象必須是Animal類型或者它的子類,否則,將無法調用run()方法。

對於Python這樣的動態語言來說,則不一定需要傳入Animal類型。我們只需要保證傳入的對象有一個run()方法就可以了:

Python學習—面向對象學習上