8--oop

oop-Python面向對象

  • Python的面向對象
  • 面向對象編程
    • 基礎
    • 共有私有
    • 繼承
    • 組合,Mixin
  • 魔法函數
    • 魔法函數概述
    • 構造類魔法函數
    • 運算類魔法函數

1.面向對象概述(ObjectOriented,00)

  • OOP思想
  • 幾個名詞
    • OO:面向對象
    • OOA:面向對象的分析
    • OOD:面向對象的設計
    • OOI:面向對象的實現
    • OOP:面向對象的編程
    • OOA->OOD->OOI:面向對象的實現過程
  • 類和對象的概念
    • 類:抽象名詞,表明一個集合,共性的事物
    • 對象:具象的事物,單個個體
    • 類跟具象的關係
      • 一個具象,表明一類事物的某一個個體
      • 一個是抽象,表明的是一大類事物
    • 類中的內容,應該具備兩個內容
      • 代表事物的特徵,叫作屬性(變量)
      • 代表事物功能或動做,稱爲成員方法(函數)

2. 類的基本實現

  • 類的命名
    • 遵照變量命名的規範
    • 大駝峯(由一個或多個單詞構成,每一個單詞首字母大寫,單詞跟單詞直接相連)
    • 儘可能避開跟系統命名類似的命名
  • 如何申明一個類
    • 必須用class關鍵字
    • 類由屬性和方法構成,其餘不容許出現
    • 成員屬性定義能夠直接使用變量賦值,若是沒有,可使用None
    • 案例 01.py

定義一個學生類,用來形容學生html

# 定義一個空的類
class Student():
    # 一個空類,pass表明直接跳過
    # 此處pass必須有
    pass

# 定義一個對象
mingyue = Student()

#在定義一個類,用來描述聽Python的學生
class PythonStudent():
    # 用None給不肯定的值賦值
    name = None
    age = 18
    course = "Python"
    
    # 須要注意
    # 1.def doHomework的縮進層級
    # 2.系統默認由一個self參數
    def doHomework(self):
        print("I 在作做業")
        
        # 推薦在函數末尾使用return語句
        return None
        
# 實例化一個叫yueyue的學生,是一個具體的人
yueyue = PythonStudent()
print(yueyue.name)
print(yueyue.age)
# 注意成員函數的調用沒有傳遞進入參數
yueyue.doHomework()
- 案例 02.py
class A():
    name = 'jjc'
    age = 18

for k,v in A.__dict__.items():
    print(k,"--",v)
  • 實例化類python

    變量 = 類名() # 實例化了一個對象
  • 訪問對象成員
    -使用點操做符
obj.成員屬性名稱
        obj.成員方法
  • 能夠經過默認內置變量檢查類和對象的全部成員
    • 對象全部成員檢查
    dict先後各有兩個下劃線
          obj.__dict__
  • 類全部的成員
class_name.__dict__

3.anaconda基本使用

- anaconda主要是一個虛擬環境管理器
- 仍是一個安裝包管理器
- conda list:顯示anaconda安裝的包
- conda  env list:顯示anconda的虛擬環境列表
- conda create -n xxx python=3.6:建立python版本爲3.6的虛擬環境,名稱爲xxx

4.類和對象的成員分析

  • 類和對象均可以存儲成員,成員能夠歸類全部,也能夠歸對象全部
  • 類存儲成員時使用的是與類關聯的一個對象
  • 對象存儲成員時是存儲在當前對象中
  • 對象訪問一個成員時,若是對象中沒有該成員,嘗試訪問類中同名的成員,若是對象中有此成員,必定使用對象中的成員
  • 創假對象的時候,類中的成員不會放人對象當中,而獲得一個空對象,沒有成員
  • 經過對象對類中成員從新賦值後者經過對象添加成員時,對應成員會保存在對象中,而不會修改類成員

5. 關於self

  • self在對象的方法中表示當前對象自己,若是,經過對象調用一個方法,那麼該對象會自動傳入到當前方法的第一個參數
  • self並非關鍵字,只是一個用於接受對象的普通參數,理論上能夠用任何一個普通變量名代替
  • 方法中有self形參的方法成爲非綁定類的方法,能夠經過對象訪問,沒有self的是綁定類的方法,只能經過類訪問
  • 使用類訪問綁定類的方法時,若是類方法中須要訪問當前的成員,能夠經過__class__成員來訪問

6. 面向對象的三大特性

  • 封裝
  • 繼承
  • 多態
6.1 封裝
  • 封裝就是對對象的成員進行訪問限制
  • 封裝的三個級別:
    • 公開,public
    • 受保護的,public
    • 私有的,public
    • public,public,public不是關鍵字
  • 判別對象的位置
    • 對象內部
    • 對象外部
    • 子類中
  • 私有
    • 私有成員是最高級別的封裝,只能在當前類或對象中訪問
    • 在成員前面添加兩個下劃線便可
    • python的私有不是真私有,是一種name mangling的更名策略,可使用對象._classname__attributename訪問
    class Person():
      # name是共有的成員
      name = 'jjc'
      # __age就是私有成員
      __age = 18
    p = Person()
    print(p._Person__age)
  • 受保護的封裝
    • 受保護的封裝是將對象成員進行必定級別的封裝,而後,在類中或者子類中均可以進行訪問,可是在外部不能夠
    • 封裝方法:在成員名稱添加一個下劃線便可
  • 公開的
    • 公開的封裝實際對成員沒有任何操做,任何地方均可以訪問
6.2 繼承
  • 繼承就是一個類能夠得到另一個類中的成員屬性和成員方法
  • 做用:減小代碼,增長代碼的複用功能,同時能夠設置類與類直接的關係
  • 繼承與被繼承的概念:
    • 被繼承的類叫父類,也叫基類,也叫超類
    • 用於繼承的類,叫子類,也叫派生類
    • 繼承與被繼承必定存在一個 is-a 關係
  • 繼承的語法
# 繼承的語法
# 在python中,任何類都有一個共同的父類叫object

class Person():
    name = 'NoName'
    age = 0
    def sleep(self):
        print('Sleeping')
# 父類寫在括號內
class Teacher(Person):
    pass
    
t = Teacher()
t.sleep()
print(t.name)
  • 繼承的特徵
    • 全部的類都繼承自object類,即全部的類都是object類的子類
    • 子類一旦繼承父類,則可使用父類中除私有成員外的全部內容
    • 子類繼承父類後並無將父類成員徹底賦值到子類中,而是經過引用關係訪問調用
    • 子類中能夠定義獨有的成員屬性和方法
    • 子類中定義的成員和父類成員相同,則優先使用子類成員
    • 子類想擴充弄父類的方法,能夠在定義新方法的同時訪問父類成員來進行代碼重用,可使用 [父類名.父類成員] 的格式來調用父類成員,也可使用 [super().父類成員] 的格式
  • 繼承變量函數的查找順序問題
    • 優先查找本身的變量
    • 沒有則查找父類
    • 構造函數若是本類中沒有定義,則自動查找調用父類構造函數
    • 若是本類有定義,則再也不繼續向上查找
# 繼承的語法
# 在python中,任何類都有一個共同的父類叫object
# 子類和父類定義同一個名稱變量,則優先使用子類自己
class Person():
    name = 'NoName'
    age = 18
    __score = 0 # 考試成績是祕密,只有本身知道
    _petname = "sec" # 小名,保護的,子類能夠用,但不公用
    def sleep(self):
        print('Sleeping')
        
    def work(self):
        print('make some money')
# 父類寫在括號內
class Teacher(Person):
    teacher_id = '9527'
    name = 'jjc'
    def make_test(self):
        print('attention')

    def work(self):
        # 擴充父類的功能只須要調用父類相應的函數
        # Person.work(self)
        # 另外一種方法,super表明父類
        Person.work(self)
        super().work()
        self.make_test()
        
t = Teacher()

t.work()
  • 構造函數
    • 是一類特殊的函數,在類進行實例化以前進行調用
    • 若是定義了構造函數,則實例化時使用構造函數,不查找父類構造函數
    • 若是沒定義,則自動查找父類構造函數
    • 若是子類沒定義,父類的構造函數帶參數,則構造對象時的參數應該按父類參數構造
# 構造函數的概念
# 繼承中的構造函數
class Animal():
    def __init__(self):
        print('Dong wu')
    
class PaxingAni(Animal):
    
    def __init__(self,name):
        print('Paxing Dongwu {0}'.format(name))

class Dog(PaxingAni):
    # __init__就是構造函數
    # 每次實例化的時候,第一個被調用
    # 由於主要工做是進行初始化,因此得名
    def __init__(self):
        print("I'm init in dog")
# 實例化的時候,括號內的參數須要跟構造函數參數匹配
# 由於找到了構造函數,則再也不查找父類的構造函數
kaka = Dog()
# 實例化Dog時,查找到Dog的構造函數,參數匹配,不報錯


# 貓沒有寫構造函數
class Cat(PaxingAni):
    pass
# 此時Cat沒有構造函數,則向上查找
# 由於PaxingAni的構造函數須要兩個參數,實例化的時候給了一個,報錯
c = Cat('jjc')
  • super
    • super不是關鍵字,而是一個類
    • super的做用是獲取MRO列表中的一個類
    • super與父類沒有實質性關係,但經過super能夠調用到父類
    • super使用的兩個方法,參見在構造函數中調用父類的構造函數
  • 單繼承和多繼承
    • 單繼承:每一個類只能繼承一個類
    • 多繼承:每一個類容許繼承多個類
  • 單繼承和多繼承的優缺點
    • 單繼承:
      • 傳承有序邏輯清晰語法簡單隱患少
      • 功能不能無限擴展,只能在當前惟一的繼承鏈中擴展
    • 多繼承:
      • 優勢:類的功能擴展方便
      • 缺點:繼承關係混亂
  • 菱形繼承/鑽石繼承問題
    • 多個子類繼承自同一個父類,這些子類遊被同一個類繼承,因而繼承關係圖星程了一個菱形
    • 關於多繼承的MRO
      • MRO就是多繼承中,用於保存繼承順序的一個列表
      • python自己採用C3算法來多繼承的菱形繼承進行計算的結果
      • MRO列表的計算原則:
        • 子類永遠在父類以前
        • 若是多個父類,則根據繼承語法中括號內類的書寫順序存放
        • 若是多個類繼承同一個父類,孫子類中只會選取繼承語法括號中第一個父類的父類
# 菱形繼承
class A():
    pass
    
class B(A):
    pass

class C(A):
    pass
    
class D(B,C):
    pass
6.3 多態
  • 多態就是同一個對象在不一樣狀況下有不痛的狀態出現
  • 多態不是語法,是一種設計思想
  • 多態性:一種調用方式,不一樣的執行效果
  • 多態:同一事物的多種形態,動物分爲人類,狗類,豬類
  • [多態及多態性[(https://www.cnblogs.com/luchuangao/p/6739557.html)算法

  • Mixin設計模式
    • 主要採用多繼承方式對類的功能進行擴展
    • 咱們使用多繼承語法來實現MIxin
    • 使用Mixin實現多繼承的時候很是當心
      • 首先他必須表示某一單一功能,而不是某個物品
      • 職責必須單一,若是由多個功能,則寫多個Mixin
      • Mixin不能依賴於子類的實現
      • 子類即便沒有繼承這個Mixin類,也能照樣工做,只是缺乏了某個功能
    • 優勢
      • 使用Mixin能夠在不對類進行任何修改的狀況下,擴展功能
      • 能夠方便的組織和維護不一樣功能組件的劃分
      • 能夠根據須要任意調整功能類的組合
      • 能夠避免建立不少新的類,致使類的繼承混亂

7. 類相關函數

  • issubclass:檢測一個類是不是另外一個類的子類
  • isinstance:檢測一個對象是不是一個類的實例
  • hasattr:檢測一個對象是否有成員XXX
  • getattr: get attribute
  • setattr: set attribute
  • delattr:delete attribute
  • dir:獲取對象的成員列表
class A():
    name = 'Jjc'
class B(A):
    pass
class C():
    pass
#檢測B是否爲A的子類
print(issubclass(B,A))
print(issubclass(B,object))
# 返回True

a=A()
print(isinstance(a,A))

print(hasattr(a,'name'))

8. 類的成員描述符(屬性)

# 屬性案例
# 建立Student類,描述學生類
# 學生具備Student.name屬性
# 但 named格式不統一

class Student():
    def __init__(self,name,age):
        self.name = name
        self.age = age
        #self.setName(name)
    def intro(self):
        print('Hi, my name is {0}'.format(self.name.upper()))
    
    def setName(self,name):
        self.name = name.title()
        
        
s1 = Student('jJC',19)
s2 = Student('wcx',18.0)
s1.intro()
s2.intro()
  • 類的成員描述符是爲了在類中對類的成員屬性進行相關操做而建立的一種方式
    • get:獲取屬性的操做
    • set:修改後者添加屬性操做
    • delete:刪除屬性的操做
  • 若是想使用類的成員描述符,大概有三種方法
    • 使用類實現描述器
    • 使用屬性修飾符
    • 使用property函數
      • property(fget, fset, fdel,doc
  • 案例
# property案例
class A():
    def __init__(self):
        self._name = 'haha'
        self._age = 18
    # 此功能,是對類變量進行讀取操做的時候應該執行的函數功能
    def fget(self):
        print("{0}被讀取了".format(self._name))
        return self._name
    # 模擬的是對變量進行寫操做的時候執行的功能
    def fset(self, val):
        print("{0}我被寫入了,可是還能夠作好多事情".format(val))
        self._name = '我是' + val
        
    # fdel模擬的是刪除變量的時候進行的操做
    def fdel(self):
        del self._name
    
    name = property(fget, fset, fdel, "這是一個property的例子")
    
a = A()
a.name = "jjc"
print(a.name)
#上面的代碼等價於下面的代碼

class A(object):
    
    @property
    def name(self):
        print("{0}被讀取了".format(self._name))
        return self._name
    
    @name.setter
    def name(self,val):
        print("{0}我被寫入了,可是還能夠作好多事情".format(val))
        self._name = '我是' + val
        
    @name.deleter
    def name(self):
        del self._name
        
a = A()
a.name = "jjc"
print(a.name)
  • 不管哪一種修飾符都是爲了隊成員屬性進行相應的控制
    • 類的方式:適合多個類中的多個屬性共用一個描述符
    • property:使用當前類中使用,能夠控制一個類中多個屬性
    • 屬性修飾符:使用於當前類中使用,控制一個類中的一個屬性

9. 類的內置屬性

- __dict__:以字典的方式顯示類的成員組成
- __doc__:獲取類的文檔信息
- __name__:獲取類的名稱,若是在模塊中使用,獲取模塊的名稱
- __bases__:獲取某個類的全部父類,以元組的方式顯示

10. 類的經常使用魔術方法

  • 魔術方法就是不須要人爲調用的方法,基本是在特定的時刻自動觸發
  • 魔術方法的統一的特徵,方法名被先後各兩個下劃線包裹
  • 操做類
    • 'init':構造函數
    • 'new':對象實例化方法
    • 'call':對象當函數使用的時候觸發
    • 'str':當對象被當作字符串使用的時候調用
    • repr:返回字符串,跟__str__
# __init__ 舉例
class A():
    def __init__(self,name = 0):
        print("sa")
    # __call__舉例
    
    def __call__(self):
        print("我被舉報了")
    
    def __str__(self):
        return 'bu'
a = A()
a()
print(a)
  • 描述符相關
    • set
    • get
    • delete
  • 屬性操做相關
    • getattr:訪問一個不存在的屬性時觸發,案例
    • setattr:對成員屬性進行設置的時候觸發,案例
      • 參數:
        • self用來獲取當前對象
        • 被設置的屬性名稱,以字符串形式出現
        • 須要對屬性名稱設置的值
      • 做用:進行屬性設置的時候進行驗證或者修改
      • 注意:在該方法中不能對屬性直接進行賦值操做,不然死循環
class A():
    def __getattr__(self,name):
        print('沒找到')
        print(name)
a = A()

print(a.name)
print(a.addr)
# __setattr__案例
class Person():
    def __init__(self):
        pass
    
    def __setattr__(self, name, value):
        print('設置屬性:{0}'.format(name))
        # 下面語句會致使問題,死循環
        #self.name = value
        # 此種狀況,爲了不死循環,規定統一調用父類魔法函數
        super().__setattr__(name,value)
p = Person()
print(p.__dict__)
p.age = 18
  • 運算分類相關魔術方法
    • gt:進行大於判斷的時候觸發的函數
      • 參數
        • self
        • 第二個參數是第二個對象
        • 返回值能夠是任意值,推薦返回布爾值
        • 案例
# __get__
class Student():
    def __init__(self,name):
        self._name = name
    
    def __gt__(self,obj):
        print("haha, {0}會比{1}大嗎?".format(self._name,obj._name))
        return self._name > obj._name
# 字符串的比較是按什麼規則
stu1 = Student('one')
stu2 = Student('two')
print(stu1 > stu2)
# 問,如何讓顯示結果美觀,one 會比 twi大嗎

11. l類和對象的三種方法

  • 實例方法
    • 須要實例化對象才能使用的方法,使用過程當中可能須要截止對象的其餘的方法完成
  • 靜態方法
    • 不須要實例化,經過類直接訪問
  • 類方法
    • 不須要實例化
  • 參看案例
# 三種方法案例
class Person:
    # 實例方法
    def eat(self):
        print(self)
        print('Eating……')
        
    # 類方法
    # 類方法的第一個參數,通常命名爲cls,區別於self
    @classmethod
    def play(cls):
        print(cls)
        print('Playing……')
        
    # 靜態方法
    # 不須要用第一個參數表示自身或者類
    @staticmethod
    def say():
        print('Saying……')
yueyue = Person()

#實例方法
yueyue.eat()
# 類方法
Person.play()
yueyue.play()
# 靜態方法
Person.say()
yueyue.say()

12.抽象類

  • 抽象方法:沒有具體實現內容的方法成爲抽象方法
  • 抽象方法的主要意義是規範了子類的行爲和接口
  • 抽象類的使用須要藉助abc模塊
    • import abc
  • 抽象類:包含抽象方法的類叫抽象類,一般成爲ABC類
  • 抽象類的使用
    • 抽象類能夠包含抽象方法,也能夠包含具體方法
    • 抽象類中能夠有方法也能夠有屬性
    • 抽象類不容許直接實例化
    • 必須繼承纔可使用,且繼承的子類必須實現全部繼承來的抽象方法
    • 假定子類沒有實現全部的抽象方法,則子類也不能實例化
    • 抽象類的主要做用是設定類的標準,以便於開發的時候具備統一的規範
# 抽象類的實現
import abc
# 申明一個類而且制定當前類的元類
class Human(metaclass = abc.ABCMeta):
    
    # 定義一個抽象的方法
    @abc.abstractmethod
    def smoking(self):
        pass
        
    # 定義類抽象方法
    @abc.abctractclassmethod
    def drink():
        pass
    
    # 定義靜態抽象方法  
    @abc.abstractstaticmethod
    def play():
        pass

13.自定義類

  • 類實際上是一個類定義和各類方法的自由組合
# 本身組裝一個類
# 若是要綁定對象,用下面一句代碼,綁定類,不須要
from types import MethodType

class A():
    pass
    
def say(self):
    print("saying……")
    
a = A()
a.tt = MethodType(say,A)
a.tt()
  • 能夠定義類和函數,而後本身經過類直接賦值
  • 能夠藉助MethodType實現
  • 藉助於type實現
  • 利用元類實現,MetaClass
    • 元類是類
    • 備用來創造別的類
# 利用type造一個類
# 先定義類應該具備的成員函數
def say(self):
    print("saying……")
def talk(self):
    print("Talking……")

# 用type來建立一個類
A = type("AName",(object,),{"class_say":say, "class_talk":talk})

# 而後能夠像正常訪問同樣使用類
a = A()

a.class_say()
a.class_talk()
# 元類演示
# 元類寫法是固定的,必須繼承自type
# 元類通常命名以MetaClass結尾
class TulingMetaClass(type):
    # 注意一下寫法
    def __new__(cls, name, bases, attrs):
        # 本身的業務處理
        print("我是元類")
        attrs['id'] = '000000'
        attrs['addr'] = "蘇州市"
        return type.__new__(cls, name, bases, attrs)

# 元類定義完就可使用,使用注意寫法
class Teacher(object, metaclass = TulingMetaClass):
    pass
    
t = Teacher()

t.id
本站公眾號
   歡迎關注本站公眾號,獲取更多信息