Python面向對象初始(三大特徵,多態,繼承,封裝)

Python面向對象的初始

面向過程的程序設計的核心是過程(流水線式思惟),過程即解決問題的步驟,面向過程的設計就比如精心設計好一條流水線,考慮周全何時處理什麼東西。python

優勢是:極大的下降了寫程序的複雜度,只須要順着要執行的步驟,堆疊代碼便可。linux

缺點是:一套流水線或者流程就是用來解決一個問題,代碼牽一髮而動全身。git

應用場景:一旦完成基本不多改變的場景,著名的例子有Linux內核,git,以及Apache HTTP Server等。程序員

面向對象的程序設計的核心是對象(上帝式思惟),要理解對象爲什麼物,必須把本身當成上帝,上帝眼裏世間存在的萬物皆爲對象,不存在的也能夠創造出來。面向對象的程序設計比如如來設計西遊記,如來要解決的問題是把經書傳給東土大唐,如來想了想解決這個問題須要四我的:唐僧,沙和尚,豬八戒,孫悟空,每一個人都有各自的特徵和技能(這就是對象的概念,特徵和技能分別對應對象的屬性和方法),然而這並很差玩,因而如來又安排了一羣妖魔鬼怪,爲了防止師徒四人在取經路上被搞死,又安排了一羣神仙保駕護航,這些都是對象。而後取經開始,師徒四人與妖魔鬼怪神仙互相纏鬥着直到最後取得真經。如來根本不會管師徒四人按照什麼流程去取。github

面向對象的程序設計的編程

優勢是:解決了程序的擴展性。對某一個對象單獨修改,會馬上反映到整個體系中,如對遊戲中一我的物參數的特徵和技能修改都很容易。設計模式

缺點是:可控性差,沒法向面向過程的程序設計流水線式的能夠很精準的預測問題的處理流程與結果,面向對象的程序一旦開始就由對象之間的交互解決問題即使是上帝也沒法預測最終結果。因而咱們常常看到一個遊戲人某一參數的修改極有可能致使陰霸的技能出現,一刀砍死3我的,這個遊戲就失去平衡.微信

應用場景:需求常常變化的軟件,通常需求的變化都集中在用戶層,互聯網應用,企業內部軟件,遊戲等都是面向對象的程序設計大顯身手的好地方。網絡

在python 中面向對象的程序設計並非所有。函數

面向對象編程可使程序的維護和擴展變得更簡單,而且能夠大大提升程序開發效率 ,另外,基於面向對象的程序可使它人更加容易理解你的代碼邏輯,從而使團隊開發變得更從容。

瞭解一些名詞:類、對象、實例、實例化

類:具備相同特徵的一類事物(人、狗、老虎)

對象/實例:具體的某一個事物(隔壁阿花、樓下旺財)

實例化:類——>對象的過程

類的相關知識:

聲明:

def functionName(args):
     '函數文檔字符串'
      函數體 


'''
class 類名:
    '類的文檔字符串'
    類體
'''

#咱們建立一個類
class Data:
    pass

屬性:

class Person:   #定義一我的類
    role = 'person'  #人的角色屬性都是人
    def walk(self):  #人均可以走路,也就是有一個走路方法
        print("person is walking...")


print(Person.role)  #查看人的role屬性
print(Person.walk)  #引用人的走路方法,注意,這裏不是在調用

實例化:類名加括號就是實例化,會自動觸發__init__函數的運行,能夠用它來爲每一個實例定製本身的特徵

 

class Person:   #定義一我的類
    role = 'person'  #人的角色屬性都是人
    def __init__(self,name):
        self.name = name  # 每個角色都有本身的暱稱;
        
    def walk(self):  #人均可以走路,也就是有一個走路方法
        print("person is walking...")


print(Person.role)  #查看人的role屬性
print(Person.walk)  #引用人的走路方法,注意,這裏不是在調用

實例化的過程就是類——>對象的過程

本來咱們只有一個Person類,在這個過程當中,產生了一個egg對象,有本身具體的名字、攻擊力和生命值。

語法:對象名 = 類名(參數)

  self

self:在實例化時自動將對象/實例自己傳給__init__的第一個參數,你也能夠給他起個別的名字,可是正常人都不會這麼作。
由於你瞎改別人就不認識

 類屬性的補充:

一:咱們定義的類的屬性到底存到哪裏了?有兩種方式查看
dir(類名):查出的是一個名字列表
類名.__dict__:查出的是一個字典,key爲屬性名,value爲屬性值

二:特殊的類屬性
類名.__name__# 類的名字(字符串)
類名.__doc__# 類的文檔字符串
類名.__base__# 類的第一個父類(在講繼承時會講)
類名.__bases__# 類全部父類構成的元組(在講繼承時會講)
類名.__dict__# 類的字典屬性
類名.__module__# 類定義所在的模塊
類名.__class__# 實例對應的類(僅新式類中)

對象的相關知識:

class 類名:
    def __init__(self,參數1,參數2):
        self.對象的屬性1 = 參數1
        self.對象的屬性2 = 參數2

    def 方法名(self):pass

    def 方法名2(self):pass

對象名 = 類名(1,2)  #對象就是實例,表明一個具體的東西
                  #類名() : 類名+括號就是實例化一個類,至關於調用了__init__方法
                  #括號裏傳參數,參數不須要傳self,其餘與init中的形參一一對應
                  #結果返回一個對象
對象名.對象的屬性1   #查看對象的屬性,直接用 對象名.屬性名 便可
對象名.方法名()     #調用類中的方法,直接用 對象名.方法名() 便可

類名稱空間與對象名稱空間:

而類有兩種屬性:靜態屬性和動態屬性

  • 靜態屬性就是直接在類中定義的變量
  • 動態屬性就是定義在類中的方法

其中類的數據屬性是共享給全部對象的

>>>id(egg.role)
4341594072
>>>id(Person.role)
4341594072

而類的動態屬性是綁定到全部對象的

>>>egg.attack
<bound method Person.attack of <__main__.Person object at 0x101285860>>
>>>Person.attack
<function Person.attack at 0x10127abf8> 

建立一個對象/實例就會建立一個對象/實例的名稱空間,存放對象/實例的名字,稱爲對象/實例的屬性

在obj.name會先從obj本身的名稱空間裏找name,找不到則去類中找,類也找不到就找父類...最後都找不到就拋出異常

面向對象的三大特徵:封裝,繼承,多態

繼承:

繼承是一種建立新類的方式,在python中,新建的類能夠繼承一個或多個父類,父類又可稱爲基類或超類,新建的類稱爲派生類或子類

python中類的繼承分爲:單繼承和多繼承

複製代碼
class ParentClass1: #定義父類
    pass

class ParentClass2: #定義父類
    pass

class SubClass1(ParentClass1): #單繼承,基類是ParentClass1,派生類是SubClass
    pass

class SubClass2(ParentClass1,ParentClass2): #python支持多繼承,用逗號分隔開多個繼承的類
    pass

查看繼承:

>>> SubClass1.__bases__ #__base__只查看從左到右繼承的第一個子類,__bases__則是查看全部繼承的父類
(<class '__main__.ParentClass1'>,)
>>> SubClass2.__bases__
(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)

提示:若是沒有指定基類,python的類會默認繼承object類,object是全部python類的基類,它提供了一些常見方法(如__str__)的實現

>>> ParentClass1.__bases__
(<class 'object'>,)
>>> ParentClass2.__bases__
(<class 'object'>,)

繼承的重要性:

==========================第一部分
例如

  貓能夠:爬樹、吃、喝、拉、撒

  狗能夠:看門、吃、喝、拉、撒

若是咱們要分別爲貓和狗建立一個類,那麼就須要爲 貓 和 狗 實現他們全部的功能,僞代碼以下:
 

#貓和狗有大量相同的內容
class 貓:

    def爬樹(self):
        print '爬樹'

    def 吃(self):
        # do something

    def 喝(self):
        # do something

    def 拉(self):
        # do something

    def 撒(self):
        # do something

class 狗:

    def 看門(self):
        print '看門'

    def 吃(self):
        # do something

    def 喝(self):
        # do something

    def 拉(self):
        # do something

    def 撒(self):
        # do something



==========================第二部分
上述代碼不難看出,吃、喝、拉、撒是貓和狗都具備的功能,而咱們卻分別的貓和狗的類中編寫了兩次。若是使用 繼承 的思想,以下實現:

  動物:吃、喝、拉、撒

     貓:爬樹(貓繼承動物的功能)

     狗:看門(狗繼承動物的功能)

僞代碼以下:
class 動物:

    def 吃(self):
        # do something

    def 喝(self):
        # do something

    def 拉(self):
        # do something

    def 撒(self):
        # do something

# 在類後面括號中寫入另一個類名,表示當前類繼承另一個類
class 貓(動物):

    def爬樹(self):
        print '爬樹'

# 在類後面括號中寫入另一個類名,表示當前類繼承另一個類
class 狗(動物):

     def 看門(self):
        print '看門'




==========================第三部分
#繼承的代碼實現
class Animal:

    def eat(self):
        print("%s 吃 " %self.name)

    def drink(self):
        print ("%s 喝 " %self.name)

    def shit(self):
        print ("%s 拉 " %self.name)

    def pee(self):
        print ("%s 撒 " %self.name)


class Cat(Animal):

    def __init__(self, name):
        self.name = name
        self.breed = ''

    def爬樹(self):
        print '爬樹'

class Dog(Animal):

    def __init__(self, name):
        self.name = name
        self.breed=''
  
    def 看門(self):
        print '看門'



# ######### 執行 #########

c1 = Cat('小白家的小黑貓')
c1.eat()

c2 = Cat('小黑的小白貓')
c2.drink()

d1 = Dog('胖子家的小瘦狗')
d1.eat()

使用繼承來重用代碼比較好的例子

繼承能夠減小代碼重用

多繼承:

  • 是否能夠繼承多個類
  • 若是繼承的多個類每一個類中都定了相同的函數,那麼那一個會被使用呢?

一、Python的類能夠繼承多個類,Java和C#中則只能繼承一個類

二、Python的類若是繼承了多個類,那麼其尋找方法的方式有兩種,分別是:深度優先廣度優先

  • 當類是經典類時,多繼承狀況下,會按照深度優先方式查找
  • 當類是新式類時,多繼承狀況下,會按照廣度優先方式查找

經典類和新式類,從字面上能夠看出一個老一個新,新的必然包含了跟多的功能,也是以後推薦的寫法,從寫法上區分的話,若是 當前類或者父類繼承了object類,那麼該類即是新式類,不然即是經典類。

class D:

    def bar(self):
        print 'D.bar'


class C(D):

    def bar(self):
        print 'C.bar'


class B(D):

    def bar(self):
        print 'B.bar'


class A(B, C):

    def bar(self):
        print 'A.bar'

a = A()
# 執行bar方法時
# 首先去A類中查找,若是A類中沒有,則繼續去B類中找,若是B類中麼有,則繼續去D類中找,若是D類中麼有,則繼續去C類中找,若是仍是未找到,則報錯
# 因此,查找順序:A --> B --> D --> C
# 在上述查找bar方法的過程當中,一旦找到,則尋找過程當即中斷,便不會再繼續找了
a.bar()

經典類多繼承

經典類多繼承
class D(object):

    def bar(self):
        print 'D.bar'


class C(D):

    def bar(self):
        print 'C.bar'


class B(D):

    def bar(self):
        print 'B.bar'


class A(B, C):

    def bar(self):
        print 'A.bar'

a = A()
# 執行bar方法時
# 首先去A類中查找,若是A類中沒有,則繼續去B類中找,若是B類中麼有,則繼續去C類中找,若是C類中麼有,則繼續去D類中找,若是仍是未找到,則報錯
# 因此,查找順序:A --> B --> C --> D
# 在上述查找bar方法的過程當中,一旦找到,則尋找過程當即中斷,便不會再繼續找了
a.bar()

新式類多繼承

新式類多繼承

經典類:首先去A類中查找,若是A類中沒有,則繼續去B類中找,若是B類中麼有,則繼續去D類中找,若是D類中麼有,則繼續去C類中找,若是仍是未找到,則報錯

新式類:首先去A類中查找,若是A類中沒有,則繼續去B類中找,若是B類中麼有,則繼續去C類中找,若是C類中麼有,則繼續去D類中找,若是仍是未找到,則報錯

注意:在上述查找過程當中,一旦找到,則尋找過程當即中斷,便不會再繼續找了

抽象類與接口類

接口類

繼承有兩種用途:

一:繼承基類的方法,而且作出本身的改變或者擴展(代碼重用)  

二:聲明某個子類兼容於某基類,定義一個接口類Interface,接口類中定義了一些接口名(就是函數名)且並未實現接口的功能,子類繼承接口類,而且實現接口中的功能

# 一:這樣很差,我要統一一下支付的規則.

class QQpay:
    def pay(self,money):
        print('使用qq支付%s元' % money)

class Alipay:
    def pay(self,money):
        print('使用阿里支付%s元' % money)

a = Alipay()
a.pay(100)

b = QQpay()
b.pay(200)

# 二,統一支付的規則 歸一化設計,統一 pay接口
class QQpay:
    def pay(self,money):
        print('使用qq支付%s元' % money)

class Alipay:
    def pay(self,money):
        print('使用阿里支付%s元' % money)

def pay(obj,money):
    obj.pay(money)

a = Alipay()
b = QQpay()

pay(a,100)
pay(b,200)

# 三,可是,來了一個野生程序員,他不知道你的約定俗成的規則,就會出問題

class QQpay:
    def pay(self,money):
        print('使用qq支付%s元' % money)

class Alipay:
    def pay(self,money):
        print('使用阿里支付%s元' % money)

class Wechatpay:
    def fuqian(self,money):
        print('使用微信支付%s元' % money)

def pay(obj,money):
    obj.pay(money)

a = Alipay()
b = QQpay()

pay(a,100)
pay(b,200)

c = Wechatpay()
c.fuqian(300)

# 四,解決方式
# 定義一個父類,什麼都不寫,只是要求繼承個人全部類有一個pay方法,這樣就制定了一個規範,這就叫作接口類,後者抽象類.
class Payment:
    def pay(self):pass

class QQpay(Payment):
    def pay(self,money):
        print('使用qq支付%s元' % money)

class Alipay(Payment):
    def pay(self,money):
        print('使用阿里支付%s元' % money)

class Wechatpay(Payment):
    def fuqian(self,money):
        print('使用微信支付%s元' % money)


def pay(obj,money):
    obj.pay(money)

a = Alipay()
b = QQpay()

pay(a,100)
pay(b,200)

c = Wechatpay()
c.fuqian(300)

#五,他仍是不知道看你這些都繼承了一個類,因此你要制定一個規範,強制他執行.
# 建立一個規範
from abc import ABCMeta,abstractmethod
class Payment(metaclass=ABCMeta):    # 抽象類 接口類  規範和約束  metaclass指定的是一個元類
    @abstractmethod
    def pay(self):pass  # 抽象方法

class Alipay(Payment):
    def pay(self,money):
        print('使用支付寶支付了%s元'%money)

class QQpay(Payment):
    def pay(self,money):
        print('使用qq支付了%s元'%money)

class Wechatpay(Payment):
    # def pay(self,money):
    #     print('使用微信支付了%s元'%money)
    def recharge(self):pass

def pay(a,money):
    a.pay(money)

a = Alipay()
a.pay(100)
pay(a,100)    # 歸一化設計:無論是哪個類的對象,都調用同一個函數去完成類似的功能
q = QQpay()
q.pay(100)
pay(q,100)
w = Wechatpay()
pay(w,100)   # 到用的時候纔會報錯



# 抽象類和接口類作的事情 :創建規範
# 制定一個類的metaclass是ABCMeta,
# 那麼這個類就變成了一個抽象類(接口類)
# 這個類的主要功能就是創建一個規範

接口類示例

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

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

歸一化使得高層的外部使用者能夠不加區分的處理全部接口兼容的對象集合——就好象linux的泛文件概念同樣,全部東西均可以當文件處理,沒必要關心它是內存、磁盤、網絡仍是屏幕(固然,對底層設計者,固然也能夠區分出「字符設備」和「塊設備」,而後作出針對性的設計:細緻到什麼程度,視需求而定)

依賴倒置原則:
高層模塊不該該依賴低層模塊,兩者都應該依賴其抽象;抽象不該該應該依賴細節;細節應該依賴抽象。換言之,要針對接口編程,而不是針對實現編程

在python中根本就沒有一個叫作interface的關鍵字,上面的代碼只是看起來像接口,其實並無起到接口的做用,子類徹底能夠不用去實現接口 ,若是非要去模仿接口的概念,能夠藉助第三方模塊:

http://pypi.python.org/pypi/zope.interface

twisted的twisted\internet\interface.py裏使用zope.interface

文檔https://zopeinterface.readthedocs.io/en/latest/

設計模式:https://github.com/faif/python-patterns

相關文章
相關標籤/搜索