1. 程式人生 > >07 python基礎--OOP

07 python基礎--OOP

文章目錄

7.1 面向物件概述(ObjectOriented,OO)

OOP思想
    接觸到任意一個任務,首先想到的是任務這個世界的構成,是由模型構成的
幾個名詞
    OO:面向物件
    OOA:面向物件的分析
    OOD:面向物件的設計
    OOI:xxx的實現
    OOP:xxx的程式設計
    OOA->OOD->OOI: 面向物件的實現過程
類和物件的概念
    類:抽象名詞,代表一個集合,共性的事物
    物件:具象的事物,單個個體
    類跟物件的關係
        一個具象,代表一類事物的某一個個體
        一個是抽象,代表的是一大類事物
類中的內容,應該具有兩個內容
    表明事物的特徵,叫做屬性(變數)
    表明事物功能或動作,稱為成員方法(函式)

7.2 類的基本實現

類的命名
    遵守變數命名的規範
    大駝峰(由一個或者多個單詞構成,每個單詞首字母大寫,單詞跟單詞直接相連)
    儘量避開跟系統命名相似的命名
你如何宣告一個類
    必須用class關鍵字
    類由屬性和方法構成,其他不允許出現
    成員屬性定義可以直接使用變數賦值,如果沒有值,允許使用None

例項化類
        變數 = 類名() ####例項化了一個物件

訪問物件成員
    使用點操作符
             obj.成員屬性名稱
             obj.成員方法

可以通過預設內建變數檢查類和物件的所有成員
    物件所有成員檢查
            # dict前後各有兩個下劃線
            obj.__dict__ 

    類所有的成員
            # dict前後各有兩個下劃線
           class_name.__dict__
# 定義一個空的類
class Student():
    # 必須使用pass佔位
    pass
# 定義一個物件
mingyue = Student()

# 在定義一個類,用來描述聽Python的學生
class PythonStudent():
    # 用None給不確定的值賦值
  name = None
  age = 18
  course = "Python"

  #  系統預設由一個self引數
  def doHomework(self):
        print("I 在做作業")
        # 推薦在函式末尾使用return語句
  return None

# 例項化一個叫yueyue的學生,是一個具體的人
yueyue = PythonStudent()
print(yueyue.name)
print(yueyue.age)
# 注意成員函式的呼叫沒有傳遞進入引數
yueyue.doHomework()

7.3 類和物件的成員分析

類和物件都可以儲存成員,成員可以歸類所有,也可以歸物件所有
類儲存成員時使用的是與類關聯的一個物件,物件儲存成員是是儲存在當前物件中
物件訪問一個成員時,如果物件中沒有該成員,嘗試訪問類中的同名成員, 
    如果物件中有此成員,一定使用物件中的成員

建立物件的時候,類中的成員不會放入物件當中,而是得到一個空物件,沒有成員
通過物件對類中成員重新賦值或者通過物件新增成員時,對應成員會儲存在物件中,而不會修改類成員
class A():
    name = 'nana'
    def stu(self):
        grade = 3
          print('今年讀高中{}年紀'.format(grade))
## 例項化
a = A()
print(A.name)
print(a.name)
print(A.__dict__)
print(a.__dict__)
print(id(A))
print(id(a))
print(id(A.name))
print(id(a.name))
print('*'*20)
a.name = 'lili'
print(A.name)
print(a.name)
print(A.__dict__)
print(a.__dict__)
print(id(A))
print(id(a))
print(id(A.name))
print(id(a.name))
>nana
>nana
>{'__module__': '__main__', 'name': 'nana', 'stu': <function A.stu at 0x000002362BF61AE8>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
>{}
>2431689178280
>2431688435640
>2431687640208
>2431687640208     ## 物件成員id與類成員id相同
********************
>nana
>lili
>{'__module__': '__main__', 'name': 'nana', 'stu': <function A.stu at 0x000002362BF61AE8>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
>{'name': 'lili'}       ## 修改後增加新屬性
>2431689178280
>2431688435640      ## A、a的id不變
>2431687640208
>2431688435448      ## 物件修改後成員id變化
## 成員檢視
class student():
    name = 'dana'
  age = 18

  def say(self):
        print('不要惹老子')

yueyue = student()
print(yueyue.name)
print(yueyue.__dict__)   ## 檢視物件成員
print(student.__dict__)  ## 檢視類成員
yueyue.say()
>dana
>{}
>{'__module__': '__main__', 'name': 'dana', 'age': 18, 'say': <function student.say at 0x017578A0>, '__dict__': <attribute '__dict__' of 'student' objects>, '__weakref__': <attribute '__weakref__' of 'student' objects>, '__doc__': None}
>不要惹老子

7.4 關於self

self在物件的方法中表示當前物件本身,如果通過物件呼叫一個方法,那麼該物件會自動傳入到當前方法
的第一個引數中 
self並不是關鍵字,只是一個用於接受物件的普通引數,理論上可以用任何一個普通變數名代替
方法中有self形參的方法成為非繫結類的方法,可以通過物件訪問, 沒有self的是繫結類的方法,只能通過類訪問
使用類訪問繫結類的方法時, 如果類方法中需要訪問當前類的成員,可以通過 __class__成員名來訪問
## self 就是一個接受物件的普通引數
class Student():
    name = "dana"
    age = 18

  # 注意say的寫法,引數由一個self
    def say(self):
        self.name = "aaaa"
        self.age = 200
        print("My name is {0}".format(self.name))
        print("My age is {0}".format(self.age))

    def sayAgain(s):
        ## 這裡用s代替self
        print("My name is {0}".format(s.name))
        print("My age is {0}".format(s.age))

yueyue = Student()
yueyue.say()
yueyue.sayAgain()
>My name is aaaa
 My age is 200
 My name is aaaa
 My age is 200
class Teacher():
    name = "dana"
    age = 19

    def say(self):
        self.name = "yaona"
        self.age = 17
        print("My name is {0}".format(self.name))
        # 呼叫類的成員變數需要用 __class__
        print("My age is {0}".format(__class__.age))

    def sayAgain():
        print(__class__.name)
        print(__class__.age)
        print('hello')

t = Teacher()
t.say()
## 呼叫繫結類函式使用類名
Teacher.sayAgain()
>My name is yaona
>My age is 19
>dana
>19
>hello
class student():
    name = 'dana'
    age = 18

    def say(self):
        self.name = 'nana'
        print('不要惹老子')

yueyue = student()
print(yueyue.name)
print(yueyue.age)
print(yueyue.say())
>dana
 18
 不要惹老子
 None   ##None是函式執行的結果, 因為它沒有return所以返回值是None

class A():
    name = " liuying"
    age = 18

    def __init__(self):
        self.name = "aaaa"
        self.age = 200

    def say(self):
        print(self.name)
        print(self.age)

class B():
    name = "bbbb"
    age = 90

a = A()
# 此時,系統會預設把a作為第一個引數傳入函式
a.say()
#
# 此時,self被a替換
A.say(a)
# 同樣可以把A作為引數傳入
A.say(A)
#
# 此時,傳入的是類例項B,因為B具有name和age屬性,所以不會報錯
A.say(B)
# 以上程式碼,利用了鴨子模型

>aaaa
>200
>aaaa
>200
>liuying
>18
>bbbb
>90

7.5 面向物件的特徵

封裝

封裝就是對物件的成員進行訪問限制

封裝的三個級別:
    公開,public
    受保護的,protected
    私有的,private
    public,private,protected不是關鍵字
判別物件的位置
    物件內部
    物件外部
    子類中
[python中下劃線使用](http://blog.csdn.net/handsomekang/article/details/40303207)

私有 private
    私有成員是最高級別的封裝,只能在當前類或物件中訪問
    在成員前面新增兩個兩個下劃線即可
    
    注:Python的私有不是真私有,是一種成為name mangling的改名策略,可以使用物件._classname_attributename訪問
    
受保護的封裝 protected
    python中所謂的“保護”指的是頂級物件相對於module的可見性
    受保護的封裝是將物件成員進行一定級別的封裝,然後,在類中或者子類中都可以進行訪問,但是在外部不可以,而Python中受“保護”的物件,跟java不太一樣,如果是類(物件)的成員,其實相當於沒保護,不管是不是誇包,都可以隨意訪問,而且繼承也不受任何限制。如果被保護的是頂級的函式或變數,那就有用了,這些函式和變數不允許被import到其他包中。
    封裝方法: 在成員名稱前新增一個下劃線即可

公開的,公共的 public
    公共的封裝實際對成員沒有任何操作,任何地方都可以訪問

繼承

繼承就是一個類可以獲得另外一個類中的成員屬性和成員方法
作用: 減少程式碼,增加程式碼的複用功能, 同時可以設定類與類直接的關係

繼承與被繼承的概念:
    被繼承的類叫父類,也叫基類,也叫超類
    用於繼承的類,叫子類,也叫派生類
    繼承與被繼承一定存在一個 is-a 關係

繼承的特徵
    所有的類都繼承自object類,即所有的類都是object類的子類
    子類一旦繼承父類,則可以使用父類中除私有成員外的所有內容
    子類繼承父類後並沒有將父類成員完全賦值到子類中,而是通過引用關係訪問呼叫
    子類中可以定義獨有的成員屬性和方法
    子類中定義的成員和父類成員如果相同,則優先使用子類成員
    子類如果想擴充父類的方法,可以在定義新方法的同時訪問父類成員來進行程式碼重用,
    可以使用 [父類名.父類成員] 的格式來呼叫父類成員,也可以使用super().父類成員的格式來呼叫

繼承變數函式的查詢順序問題
    優先查詢自己的變數
    沒有則查詢父類
    建構函式如果本類中沒有定義,則自動查詢呼叫父類建構函式
    如果本類有定義,則不在繼續向上查詢

建構函式
    是一類特殊的函式,在類進行例項化之前進行呼叫
    如果定義了建構函式,則例項化時使用建構函式,不查詢父類建構函式
    如果沒定義,則自動查詢父類建構函式
    如果子類沒定義,父類的建構函式帶引數,則構造物件時的引數應該按父類引數構造

super
    super不是關鍵字, 而是一個類
    super的作用是獲取MRO(MethodResolustionOrder)列表中的第一個類
    super於父類直接沒任何實質性關係,但通過super可以呼叫到父類
    super使用兩個方,參見在建構函式中呼叫父類的建構函式

單繼承和多繼承
    單繼承:每個類只能繼承一個類
    多繼承:每個類允許繼承多個類
    優缺點
        單繼承:
            傳承有序邏輯清晰語法簡單隱患少
            功能不能無限擴充套件,只能在當前唯一的繼承鏈中擴充套件
        多繼承:
            優點:類的功能擴充套件方便
            缺點:繼承關係混亂

菱形繼承/鑽石繼承問題
    多個子類繼承自同一個父類,這些子類由被同一個類繼承,於是繼承關係圖形成一個菱形圖譜
    [MRO](https://www.cnblogs.com/whatisfantasy/p/6046991.html)
    關於多繼承的MRO
        MRO就是多繼承中,用於儲存繼承順序的一個列表
        python本身採用C3演算法來多多繼承的菱形繼承進行計算的結果
        MRO列表的計算原則:
            子類永遠在父類前面
            如果多個父類,則根據繼承語法中括號內類的書寫順序存放
            如果多個類繼承了同一個父類,孫子類中只會選取繼承語法括號中第一個父類的父類

建構函式
    在物件進行例項化的時候,系統自動呼叫的一個函式叫建構函式,通常此函式用來對例項物件進行初始化,顧名
    建構函式一定要有,如果沒有,則自動向上查詢,按照MRO順序,直到找到為止

多型

多型就是同一個物件在不同情況下有不同的狀態出現
多型不是語法,是一種設計思想
多型性: 一種呼叫方式,不同的執行效果
多型: 同一事物的多種形態,動物分為人類,狗類,豬類
[多型和多型性](https://www.cnblogs.com/luchuangao/p/6739557.html)

Mixin設計模式
    主要採用多繼承方式對類的功能進行擴充套件
    [Mixin概念](https://www.zhihu.com/question/20778853)
    [Mixin的理解](https://blog.csdn.net/u010377372/article/details/70256043)
    [MRO and Mixin](http://blog.csdn.net/robinjwong/article/details/48375833)
    [Mixin模式](https://www.cnblogs.com/xybaby/p/6484262.html)
    [Mixin MRO](http://runforever.github.io/2014-07-19/2014-07-19-python-mixin%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/)
    [MRO](http://xiaocong.github.io/blog/2012/06/13/python-mixin-and-mro/)

我們使用多繼承語法來實現Minxin
使用Mixin實現多繼承的時候非常小心
    首先他必須表示某一單一功能,而不是某個物品
    職責必須單一,如果由多個功能,則寫多個Mixin
    Mixin不能依賴於子類的實現
    子類及時沒有繼承這個Mixin類, 也能照樣工作,只是缺少了某個功能
優點
    使用Mixin可以在不對類進行任何修改的情況下,擴充功能
    可以方便的組織和維護不同功能元件的劃分
    可以根據需要任意調整功能類的組合
    可以避免建立很多新的類,導致類的繼承混亂

封裝

# 私有變數案例
class Person():
    # name是共有的成員
    name = "liuying"
    # __age就是私有成員
    __age = 18

p = Person()
# name是公有變數
print(p.name)
# __age是私有變數,直接訪問報錯
print(p.__age)
# name mangling技術
print(p._Person__age)    ## 私有變數實際改名為_Person__age
>liuying
>AttributeError: 'Person' object has no attribute '__age'
>18

繼承

# 繼承的語法
# 在python中,任何類都有一個共同的父類叫object

class Person():
    name = "NoName"
    age = 80
    __score = 0 # 考試成績是祕密,只要自己知道
    _petname = "sec" # 小名,是保護的,子類可以用,但不能公用

    def sleep(self):
        print("Sleeping ... ...")
    def work(self):
        print('make some money')

# 父類寫在括號內
class Teacher(Person):
    teacher_id = "9527"
    age = 18

    def make_test(self):
        print("attention")

    def work(self):
        # 擴充父類的功能只需要呼叫父類相應的函式
        # Person.work(self) # 擴充父類的另一種方法 # super代表得到父類  super().work()
        self.make_test()

t = Teacher()
print(t.name)
print(t.age)
print(t._petname)

t.sleep()
print(t.teacher_id)
t.make_test()
t.work()

>NoName
>18
>sec
>Sleeping ... ...
>9527
>attention
>make some money
>attention

繼承中的建構函式

class Animel():
    def __init__(self):
        print("Animel")

class PaxingAni(Animel):
    def __init__(self):
        print("Paxing Dongwu")

class Dog(PaxingAni):
    # __init__就是建構函式
    # 每次例項化的時候,第二個被自動的呼叫(第一個為__new__) # 因為主要工作是進行初始化,所以得名
    def __init__(self):
        print("I am init in dog")

# 例項化的時候,自動呼叫了Dog的建構函式
# 因為找到了建構函式,則不在查詢父類的建構函式
kaka = Dog()

# 貓沒有寫建構函式
class Cat(PaxingAni):
    pass

# 此時應該自動呼叫建構函式,因為Cat沒有建構函式,所以查詢父類建構函式
# 在PaxingAni中查詢到了建構函式,則停止向上查詢
c = Cat()
>I am init in dog
>Paxing Dongwu‘

********************************************************

# 建構函式的呼叫順序
class A():
    def __init__(self):
        print("A")

class B(A):
    def __init__(self, name):
        print("B")
        print(name)

class C(B):
    # c中想擴充套件B的建構函式,
 # 即呼叫B的建構函式後在新增一些功能 # 由兩種方法實現
    '''
 # 第一種是通過父類名呼叫 
    def __init__(self, name): 
    # 首先呼叫父類建構函式 
    B.__init__(self, name) 
    # 其次,再增加自己的功能 
    print("這是C中附加的功能") 
    '''
  # 第二種,使用super呼叫
    def __init__(self, name):
        # 首先呼叫父類建構函式
    super(C, self).__init__(name)
        # 其次,再增加自己的功能
    print("這是C中附加的功能")

# 此時,首先查詢C的建構函式
# 如果沒有,則向上按照MRO順序查詢父類的建構函式,知道找到為止
# 此時,會出現引數結構不對應錯誤
c = C("我是C")

>B
>我是C
>這是C中附加的功能

多繼承

# 多繼承的例子
# 子類可以直接擁有父類的屬性和方法,私有屬性和方法除外

class Base(object):
    def test(self):
        print("------base")

class A(Base):
    def test1(self):
        print("------test1")

class B(Base):
    def test2(self):
        print("------test2")

class C(A, B):
    pass

c = C()
c.test1()
c.test2()
c.test()
>------test1
>------test2
>------base

多型

# 多型:同一種事物的多種形態,動物分為人類,豬類(在定義角度)
class Animal:
    def run(self):
        raise AttributeError('子類必須實現這個方法')

class People(Animal):
    def run(self):
        print('人正在走')

class Pig(Animal):
    def run(self):
        print('pig is walking')

class Dog(Animal):
    def run(self):
        print('dog is running')

peo1 = People()
pig1 = Pig()
d1 = Dog()

peo1.run()
pig1.run()
d1.run()
>人正在走
>pig is walking
>dog is running

Mixin

# 例項1
## 未使用Mixin
class Person():
    name = "liuying"
    age = 18
    def eat(self):
        print("EAT.......")

class Teacher(Person):
    def work(self):
        print("Work")

class Student(Person):
    def study(self):
        print("Study")

class Tutor(Teacher, Student):
    pass

t = Tutor()

print(Tutor.__mro__)
print(t.__dict__)
print(Tutor.__dict__)
>(<class '__main__.Tutor'>, <class '__main__.Teacher'>, <class '__main__.Student'>, <class '__main__.Person'>, <class 'object'>)
>{}
>{'__module__': '__main__', '__doc__': None}
## 使用Mixin
class Person():
    name = "liuying"
    age = 18
    def eat(self):
        print("EAT.......")

class TeacherMixin():
    def work(self):
        print("Work")

class StudentMixin():
    def study(self):
        print("Study")

class Tutor(Person, TeacherMixin, StudentMixin):
    pass

t = Tutor()
print(Tutor.__mro__)
print(t.__dict__)
print(Tutor.__dict__)
>(<class '__main__.Tutor'>, <class '__main__.Person'>, <class '__main__.TeacherMixin'>, <class '__main__.StudentMixin'>, <class 'object'>)
>{}
>{'__module__': '__main__', '__doc__': None}

7.6 類相關函式

issubclass:檢測一個類是否是另一個類的子類
isinstance:檢測一個物件是否是一個類的例項
hasattr:檢測一個物件是否由成員xxx
getattr: get attribute
setattr: set attribute
delattr: delete attribute
dir: 獲取物件的成員列表
# getattr()/setattr()/delattr()/hasattr()

class A():
    name = 'lili'
  age =18

a = A()

print(hasattr(a,'name'))    ## 判斷a物件是否有name屬性
print(hasattr(A,'name'))

print(getattr(a,'name'))   ## 獲取物件a的name屬性值
setattr(a,'name','lucy')   ##設定那麼的屬性值為lucy
print(getattr(a,'name'))
delattr(A,'name')          ## 刪除屬性
print(getattr(A,'name','not found'))
delattr(a,'name')
print(getattr(a,'name','not found'))     ## 讀取是否有屬性name,沒有則返回default值'not found'


>True
>True
>lili
>lucy
>not found
>not found

7.7 類的成員描述符(屬性)

[描述符類](https://www.cnblogs.com/jessonluo/p/4758662.html)
類的成員描述符是為了在類中對類的成員屬性進行相關操作而建立的一種方式
    get: 獲取屬性的操作
    set:修改或者新增屬性操作
    delete: 刪除屬性的操作
如果想使用類的成員描述符,大概有三種方法
    使用類實現描述器
    使用屬性修飾符
    使用property函式
        property函式很簡單
        property(fget, fset, fdel, doc)
    案例參看notebook
無論哪種修飾符都是為了對成員屬性進行相應的控制  
    類的方式: 適合多個類中的多個屬性共用用一個描述符
    property:使用當前類中使用,可以控制一個類中多個屬性
    屬性修飾符: 使用於當前類中使用,控制一個類中的一個屬性
# peroperty案例
# 定義一個Person類,具有name,age屬性
# 對於任意輸入的姓名,我們希望都用大寫方式儲存
# 年齡,我們希望內部統一用整數儲存
# x = property(fget, fset, fdel, doc)
class Person():
    '''
 這是一個人,一個高尚的人,一個脫離了低階趣味的人 他還他媽的有屬性 '''
  # 函式的名稱可以任意
  def fget(self):
        return self._name * 2

  def fset(self, name):
        # 所有輸入的姓名以大寫形式儲存
  self._name = name.upper()

    def fdel(self):
        self._name = "NoName"

  name = property(fget, fset, fdel, "對name進行下下操作啦")

p1 = Person()
p1.name = "TuLing" ## 呼叫fset()
print(p1.name)            ## 呼叫fget()
print(p1.__dict__)        ## 獲取類的成員組成
print(p1.__doc__)         ## 獲取類的文件資訊
print(Person.__name__)    ## 獲取類的名稱
print(Person.__bases__)   ## 獲取所有父類

>TULINGTULING
>{'_name': 'TULING'}
>
    這是一個人,一個高尚的人,一個脫離了低階趣味的人
    他還他媽的有屬性
    
>Person
>(<class 'object'>,)

7.8 類的內建屬性

__dict__:以字典的方式顯示類的成員組成
__doc__: 獲取類的文件資訊
__name__:獲取類的名稱,如果在模組中使用,獲取模組的名稱
__bases__: 獲取某個類的所有父類,以元組的方式顯示

7.9 類的常用魔術方法

魔術方法就是不需要人為呼叫的方法,基本是在特定的時刻自動觸發
魔術方法的統一的特徵,方法名被前後各兩個下滑線包裹
操作類
    __init__: 建構函式,用於初始化一個類
    __new__: 物件例項化方法,此函式較特殊,一般不需要使用
    __call__: 物件當函式使用的時候觸發
    __str__: 當物件被當做字串使用的時候呼叫
    __repr__: 返回字串,跟__str__具體區別請百度
描述符相關
    __set__
    __get__
    __delete__
屬性操作相關
    __getattr__: 訪問一個不存在的屬性時觸發
    __setattr__: 對成員屬性進行設定的時候觸發
        引數: 
            self用來獲取當前物件
            被設定的屬性名稱,以字串形式出現
            需要對屬性名稱設定的值
        作用:進行屬性設定的時候進行驗證或者修改
        注意: 在該方法中不能對屬性直接進行賦值操作,否則死迴圈
        參看案例
運算分類相關魔術方法
    __gt__: 進行大於判斷的時候觸發的函式
        引數:
            self
            第二個引數是第二個物件
            返回值可以是任意值,推薦返回布林值
class Person():
    def __init__(self):
        print('直接被呼叫了')

    def __call__(self, *args, **kwargs):
        print('使用函式時被呼叫')

    def __str__(self):
        return "當物件被當做字串使用的時候呼叫"
p = Person()
p()
print(p)
>直接被呼叫了
>使用函式時被呼叫
>當物件被當做字串使用的時候呼叫

# __gt__s
class Student():
    def __init__(self, name):
        self._name = name

    def __gt__(self, obj):
        print("哈哈, {0} 會比 {1} 大嗎?".format(self, obj))
        return self._name > obj._name

stu1 = Student("one")
stu2 = Student("two")
print(stu1 > stu2)

>哈哈, <__main__.Student object at 0x0000022B5112FEB8> 會比 <__main__.Student object at 0x0000022B51121A58> 大嗎?
>False

7.10 類和物件的三種方法

例項方法
    需要例項化物件才能使用的方法,使用過程中可能需要截止物件的其他物件的方法完成
靜態方法
    不需要例項化,通過類直接訪問
類方法
    不需要例項化
參看案例
三個方法具體區別自行百度
# 三種方法的案例
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()
><__main__.Person object at 0x00000299FFF61B70>
>Eating.....
><class '__main__.Person'>
 Playing.....
><class '__main__.Person'>
 Playing.....
>Saying....
>Saying....

# 屬性的三種用法
# 1. 賦值
# 2. 讀取
# 3. 刪除
class A():
    def __init__(self):
        self.name = "haha"
  self.age = 18

a = A()

a.name = "Lili"
print(a.name)
del a.name
# print(a.name)    # 報錯
>Lili

# 類屬性 property
# 對變數除了普通的三種操作,還想增加一些附加的操作,那麼可以通過property完成
class A():
    def __init__(self):
        self.name = "haha"
  self.age = 18

  # 此功能,是對類變數進行讀取操作的時候應該執行的函式功能
  def fget(self):
        print("我被讀取了")
        return self.name

    # 模擬的是對變數進行寫操作的時候執行的功能
  def fset(self, name):
        print("我被寫入了,但是還可以左好多事情")
        self.name = "圖靈學院:" + name

    # fdel模擬的是刪除變數的時候進行的操作

  def fdel(self):
        pass

  # property的四個引數順序是固定的
  # 第一個引數代表讀取的時候需要呼叫的函式
  # 第二個引數代表寫入的時候需要呼叫的函式
  # 第三個是刪除
  name2 = property(fget, fset, fdel, "這是一個property的例子")

a = A()
print(a.name)
print(a.name2)
>haha
>我被讀取了
 haha

7.11 抽象類

[抽象類]http://www.cnblogs.com/asaka/p/6758426.html
抽象方法: 沒有具體實現內容的方法成為抽象方法
抽象方法的主要意義是規範了子類的行為和介面
抽象類的使用需要藉助abc模組
       import abc
抽象類:包含抽象方法的類叫抽象類,通常成為ABC類
抽象類的使用
    抽象類可以包含抽象方法,也可以包含具體方法
    抽象類中可以有方法也可以有屬性
    抽象類只能被繼承,不能被例項化,繼承的子類必須實現所有繼承來的抽象方法
    假定子類沒有是現實所有繼承的抽象方法,則子類也不能例項化
    抽象類的主要作用是設定類的標準,以便於開發的時候具有統一的規範
import abc
class All_file(metaclass=abc.ABCMeta):
    all_type = 'file'
  @abc.abstractmethod #定義抽象方法,無需實現功能
  def read(self):
        '子類必須定義讀功能'
  pass

  @abc.abstractmethod
  def write(self):
        '子類必須定義寫功能'
  pass

class Txt(All_file): #子類繼承抽象類,但是必須定義read和write方法
  def read(self):
        print('文字資料的讀取')

    def write(self):
        print('文字資料的讀取方法')

class Process(All_file):  # 子類繼承抽象類,但是必須定義read和write方法

  def read(self):
        print('程序資料的讀取方法')

    def write(self):
        print('程序資料的讀取方法')

wenbenwenjian=Txt()
wenbenwenjian.read()
jinchengwenjian=Process()
jinchengwenjian.read()

print(wenbenwenjian.all_type)
print(jinchengwenjian.all_type)

>文字資料的讀取
 程序資料的讀取方法
 file
 file

7.12 自定義類

類其實是一個類定義和各種方法的自由組合
可以定義類和函式,然後自己通過類直接賦值
可以藉助於MethodType實現
藉助於type實現

元類:http://python.jobbole.com/88795/
利用元類實現MetaClass
    元類是類
    備用來創造別的類
# 函式名可以當變數使用
def Person(name):
    print('我的名字是%s'%name)


Person('lili')

s = Person

s('lucy')

>我的名字是lili
 我的名字是lucy


# 自己組裝一個類
class A():
    pass

def say(self):
    print('saying......')

A.say = say   ## 組裝

a = A()   ## 例項化
a.say()   ## 呼叫方法
>saying......
--------------------------------------------------------------------------
# 利用type造一個類

# 先定義類應該具有的成員函式
def say(self):
    print('saying...')

def eat(self):
    print('eating...')
#用type來建立一個類
A = type('name', (object,), {'class_say':say,'class_eat':eat})  ## 組裝類A

# 訪問類
a = A()
a.class_say()
a.class_eat()

>saying...
 eating...

--------------------------------------------------------------------------
# 元類演示

# 元類寫法是固定的,必須繼承自type,命名以MetaClass結尾
class AMetaClass(type):
    # 注意以下寫法
    def __new__(cls, name, bases, attrs):
        # 自己的業務處理
        print("哈哈,我是元類呀")
        attrs['id'] = '000000'
        attrs['addr'] = "北京海淀區公主墳西翠路12號"
        return type.__new__(cls, name, bases, attrs)


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


t = Teacher()
>哈哈,我是元類呀