中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

面向對象編程基礎

發布時間:2020-08-03 08:13:24 來源:網絡 閱讀:3414 作者:長跑者1號 欄目:編程語言

一 編程的分類:

1 分類

1 面向過程編程
以指令為核心:圍繞“正在發生什么”進行編寫
面向過程:程序= 算法+ 數據結構
面向過程編程:程序具有一系列線性步驟,主體思想是代碼作用于數據,以指令為核心,核心是設計算法,
2 面向函數的編程
3 面向對象的編程

2 面向對象編程

面向對象編程--object oriented programming ,簡稱OOP,把對象作為程序的基本單元,一個對象包含了數據和操作數據的函數
面向過程把函數繼續切分稱為子函數,來降低系統的復雜度。
面向對象編程(OOP)
程序 = 指令 + 數據/算法 + 數據結構
以數據為核心: 圍繞“將影響誰”進行編寫
面向對象編程(OOP):圍繞數據以及為數據嚴格定義的接口來組織程序,用數據控制對代碼的訪問,面向數據,以及能夠對這些數據采取的操作進行,代碼執行過成,不能跳過數據
面向對象編程的核心概念:
所有編程語言的最終目的都是提供一種抽象方法
在機器模型(“解空間”或“方案空間”)與實際解決問題的問題模型(“問題空間”)之間,程序員必須建立一種聯系
將問題空間中的元素以及他們在解空間中的標識物抽象為對象,并允許通過問題來描述問題而不是通過方案來描述問題
可以把實例(某個類的實例)想象成一種新型變量,他保存這數據,但可以對自身數據執行操作,實例可以對自身數據做操作

二 類和實例概念

1 類:

定義了被多個同一類型對象共享的結構和行為(數據和代碼)
類是抽象的,實例是具體的,類是(概念,抽象的概念模型),類本身是不能被操作的,須有實例化的對象才能操作
類內部有兩個核心成員:代碼和數據,都叫類成員
數據:成員變量或實例變量
成員方法: 簡稱為方法,是操作數據的代碼,用于定義如何使用成員變量,因此一個類的行為和接口是通過方法來定義的
類:將同一種具體的物事的共同特性抽想出來的表現

2 實例:

實例是類的具體表現形式,類存在的目的就是為了實例化。類是抽象的,實例是具體的

三 類基本操作

1 類的定義

calss 類名(父類):
類的內容
類后面有括號的稱為新式類,沒有括號稱為經典類。
括號里面的內容是父類的名稱,程序中,所有父類都是object

面向對象編程基礎
類的查看:
面向對象編程基礎

2 類的數據屬性(類變量)

面向對象編程基礎
當類的實例化后,類的屬性是不變的
面向對象編程基礎

類的數據屬性是可變的,當在實例處修改類的數據屬性時,其類本身的數據屬性不變。

面向對象編程基礎

當類的數據屬性發生改變時,原本已經改變的實例的數據屬性不會再次改變,而新創建的實例的數據屬性會發生改變

面向對象編程基礎

#!/usr/local/bin/python3.6
#coding:utf-8
class  MyClass:
    '''is  example class'''
    x='abc'  # 類的屬性
    def  foo(self):  # 類的屬性,也是方法
        return (self,'MyClass')

myclass=MyClass()  # 進行實例化操作
print (myclass.x)  #調用類的屬性
print (MyClass.x) # 類調用類屬性
print (MyClass.foo(1))  # 類調用類方法
print (myclass.foo())  # 調用類的方法
print (type(myclass))  # 打印類型
print (id(MyClass))  # 打印類的實例地址
print (id(myclass))  # 打印類的內存地址

結果如下
面向對象編程基礎

其實例化都的內存地址和類本身的內存地址不同,且實例可以調用類的屬性和方法,類自身也可以進行相關的調用處理

3 類的方法===函數

在類中定義的函數教方法,類的方法中,要求第一個形參必須是self,而self實際上是類實例化后的對象本身
實例化后獲得的實例,是不同的實例,即使使用相同的參數實例化,也得到不一樣的對象,其會先調用new 進行實例化,然后調用_init_ 進行初始化
實例化后,會自動調用__init__方法,這個方法的第一個參數必須留給self,其他參數隨意

類方法的定義
面向對象編程基礎
類方法的調用
面向對象編程基礎

#!/usr/local/bin/python3.6
#coding:utf-8
class  MyClass:
    '''is  example class'''
    x='abc'  # 類的屬性
    y='cbd'
    def  __init__(self): # 初始化時進行打印,并顯示結果,其__init__方法中不能return,其只能用做初始化
        print ('init')
    def  foo(self):
        return (self.x)
    def  foo1(self):
        return  (self.y)
myclass=MyClass()  # 對象實例化
print (myclass.foo())
print (myclass.foo1())

結果如下

面向對象編程基礎

#!/usr/local/bin/python3.6
#coding:utf-8
class  Person:
    def  __init__(self,name,age=20):
        self.name=name  # 此處的name始終和上述的self后面的name相對應,下面相同
        self.x=age
    def  showage(self):
        print  ('{} is {}'.format(self.name,self.x))  # 此處self.x 始終和上面的相同,其中age相當于形參
tom=Person('tom')  #對參數進行實例化操作
jerry=Person('jerry',10)  # 對參數進行實例化
tom.showage() #打印其方法
jerry.showage()

a=Person('a') # 其獲取到的參數完全相同
b=Person('a')
print (a is  b) # 其內存地址不同
print  (a==b) # 其獲取結果不同,

結果如下

面向對象編程基礎

#!/usr/local/bin/python3.6
#coding:utf-8
class  Person:
    x='abc'
    def  __init__(self,name,age=18):
        self.name=name
        self.age=age
    def show(self,x,y):  # 通過此處的參數,可修改類中的屬性
        self.name=x  # 修改name的屬性
        self.age=y
        Person.x=x  # 修改類屬性
        print   (self.name,self.age,x)

tom=Person('tom')
tom.show('jerry',20)
print  (Person.x)  # 其返回結果為修改類屬性后的結果

結果如下
面向對象編程基礎

4 類的特殊屬性

1 簡介

1 class._name_ 類的名稱顯示

面向對象編程基礎

2 class._doc_ 類的幫助文檔

3 class._base_ 類的父類/基類

面向對象編程基礎

4 class._module_ 類的模塊,當不是導入的模塊的類時,其執行結果為main,當為模塊導入時,其執行結果為模塊名

面向對象編程基礎

5 class._dict_ 對象屬性的字典,用于遍歷和查找屬性,每個對象中保存這自己的屬性,類的屬性在類字典中,實例的屬性在實例的字典中

6 class._class_ 對象的類型,__class__和type中的類型是相同的,在python3中相同,在早期的python2 中有某些事不相同的

7 class._qualname_ 類的限定名

2 操作

#!/usr/local/bin/python3.6
#coding:utf-8
class  Person:
    x='abc'
    def  __init__(self,name,age=18):
        self.name=name
        self.age=age
    def show(self,x,y):  # 通過此處的參數,可修改類中的屬性
        self.name=x  # 修改name的屬性
        self.age=y
        Person.x=x  # 修改類屬性
        print   (self.name,self.age,x)
tom=Person('tom')
print  (tom.__class__,Person.__class__)  # 打印實例類型和類的類型
print (tom.__dict__,Person.__dict__)  # 打印實例的屬性字典和類的屬性字典
print  (tom.__qualname__,Person.__qualname__)  # 打印實例的限定名和類的限定名,默認的,只有類有限定名

結果如下
面向對象編程基礎

如需輸出,則需要

#!/usr/local/bin/python3.6
#coding:utf-8
class  Person:
    x='abc'
    def  __init__(self,name,age=18):
        self.name=name
        self.age=age
    def show(self,x,y):  # 通過此處的參數,可修改類中的屬性
        self.name=x  # 修改name的屬性
        self.age=y
        Person.x=x  # 修改類屬性
        print   (self.name,self.age,x)
tom=Person('tom')
print  (tom.__class__.__qualname__)  #此處通過__class__吊起該實例的類,通過類調用其限定名即可
print  (isinstance(tom,Person)) #通過此處判斷該類是否是該實例的類
print(isinstance(tom,tom.__class__))

結果
面向對象編程基礎

#!/usr/local/bin/python3.6
#coding:utf-8
class  Person:
    x='abc'
    def  __init__(self,name,age=18):
        self.name=name
        self.age=age
    def show(self,x,y):  # 通過此處的參數,可修改類中的屬性
        self.name=x  # 修改name的屬性
        self.age=y
        Person.x=x  # 修改類屬性
        print   (self.name,self.age,x)
tom=Person('tom')
jerry=Person('jerry',20)
print (tom.__dict__,jerry.__dict__,Person.__dict__,sep='\n')# 打印實例和類的屬性字典

結果如下

面向對象編程基礎

#!/usr/local/bin/python3.6
#coding:utf-8
class  Person:
    x='abc'
    age=3
    height=170
    def  __init__(self,name,age=18):
        self.name=name
        self.age=age
tom=Person('tom')
jerry=Person('jerry',20)
Person.age=30
print  (Person.__dict__,tom.__dict__,jerry.__dict__,sep='\n')  #此處打印類和實例對象屬性字典
print (Person.age,tom.age,jerry.age)  # 結果為30,18,20

Person.height+=30
print  (Person.__dict__,tom.__dict__,jerry.__dict__,sep='\n')
print (Person.height,tom.height,jerry.height) # 結果為200,200,200

tom.height=180  # 當此處給單獨的對象添加了類的屬性并賦值后,其對象tom的屬性字典中的值也會隨之變化
print  (Person.__dict__,tom.__dict__,jerry.__dict__,sep='\n') # 此處tom的字典屬性中也增加了height的key,且其值為180
print (Person.height,tom.height,jerry.height)  #結果為200,180,200

jerry.height+=30 # 此處添加了實例的屬性后,其對象字典中的對象屬性也會隨之增加
print  (Person.__dict__,tom.__dict__,jerry.__dict__,sep='\n')  # 此處jerry 的字典屬性中也增加了height的key,且其值為230
print (Person.height,tom.height,jerry.height) # 結果為200,180,230

結果如下

面向對象編程基礎

#!/usr/local/bin/python3.6
#coding:utf-8
class  Person:
    x='abc'
    age=3
    height=170
    def  __init__(self,name,age=18):
        self.name=name
        self.age=age
tom=Person('tom')
print  (tom.__dict__['name'],tom.__dict__['age'])  # 通過key調用對應的value,此處不能調用height,因為其不在tom屬性字典中

結果如下
面向對象編程基礎

3 結論:

是類的,也是這個類所有實例的,其實例都可以訪問到,是實例的,就是這個實例自己的,通過類訪問不到。

實例可以動態的給自己增加一個屬性,實例.dict[變量名]和實例.變量名都可以訪問到

實例的同名變量會隱藏這類變量,或者說是覆蓋了這類變量

實例屬性的查找順序
指的是實例使用.來訪問屬性,會先找到自己的dict,如果沒有。則通過屬性class找到自己的類,再去類的dict中找

注意: 如果實例使用dict[變量名]訪問變量,將不會按照上面的順序查找變量了

5 類方法和靜態方法

1 類方法

通過類方法@classmethod 進行將類本身調用,其中cls 關鍵字就是表示類本身,
可以使用類名.類方法(參數) 的形式進行調用。并使用return 返回結果

面向對象編程基礎

1 在定義類中,使用@classmethod 裝飾器修飾的方法
2 必須至少傳遞一個參數,且第一個參數留給了cls,cls指代調用者及類對象自身
3 cls這個標識符可以是任何合法名稱,但是為了易讀,請不要修改
4 通過cls可以直接操作類的屬性,其無法對實例的屬性進行修改,因為其第一個傳遞的是類對象,類中也不能調用實例的方法

相關實現

#!/usr/local/bin/python3.6
#coding:utf-8
class  Document:  # 父類
    def __init__(self,content):
        self.content=content

    def  print(self):  # 共有的屬性
        print (self.content)
class Word(Document):
    pass  
class PrintableWord(Word):
    def print(self): # 子類自己的私有屬性
        print ("Word pinrt {}".format(self.content))
class  Pdf(Document):
    pass  # 子類自定義私有屬性
print (PrintableWord.mro())
word=PrintableWord('test\nabc')
word.print()

結果如下

面向對象編程基礎

實例如下

#!/usr/bin/poython3.6
#conding:utf-8
class MyClass:
    x='123'
    @classmethod  # 類方法,此處傳入的第一個值是類,cls.__name__調用的是這個類的名稱,而cls.x調用的是這個類的私有屬性
    def  my(cls):
        cls.pas='456'  # 可通過此種方式在類中增加屬性
        print ('{}.x={}'.format(cls.__name__,cls.x),cls.pas)

MyClass.my()  # 此處是類的自調用
a=MyClass() # 實例化,其類的相關屬性依然存在
a.my()

結果如下
面向對象編程基礎

2 靜態方法

1 在定義類中,使用@staticmethod 裝飾器裝飾的方法
2 調用時,不會隱式傳入參數
3 靜態方法,只是表明這個方法屬于這個名稱空間,函數歸類在一起,方便管理組織

面向對象編程基礎

實例如下

#!/usr/bin/poython3.6
#conding:utf-8
class MyClass:
    def  char():  #此處無語法錯誤,但其不建議如此寫入
        print  ('char')
    @staticmethod  # 此處是靜態方法,用于保存類中的靜態變量,其本身和實例無任何關系
    def char1():
        print ('char1')
a=MyClass()
# a.char() # 此處調用會出錯,但下面的由于是靜態方法,因此其調用不會出錯
a.char1()

總結: 靜態方法和類方法都屬于類的屬性

此處不推薦直接使用def char()的方式進行處理,而推薦使用@staticmethod,python中對靜態方法的使用較少,

datetime 是在類上調用的方法,進而造出datetime對象而返回這個方法

python中常見的三種函數:
1 和實例相關的函數
2 和類相關的函數
3 一般的函數

類的方法在類創建完成后即可進行調用,而實例的方法在類創建完成之后不能調用

3 總結:

類的方法調用
類幾乎可以調用所有內部定義的方法,但是調用普通的方法時會報錯,原因是第一個參數必須是類的實例
實例幾乎可以調用所有的方法,普通的函數的調用一般不可能出現,因為不允許這么定義
類方法:默認第一個參數是類本身,其可以被類調用,不能被實例調用
普通方法:默認第一個參數是實例本身,可以被類和實例進行調用
靜態方法:默認第一個參數數傳入的參數。只能被類本身進行調用,不能被實例調用


總結:
類除了普通方法外都可以調用,普通方法需要對象的實例作為第一參數
實例可以調用所有類中的方法(包括類方法,靜態方法),普通方法傳入實例自身,靜態方法和類方法需要找到實例的類后進行相關的處理。

4 在類中的應用:

面向對象編程基礎
擴展:
面向對象編程基礎

在類中調用類本身的方法:
傳統的輸入年、月、日、的方法:

面向對象編程基礎

通過函數進行格式化的結果,通過在函數中調用類的方式實現

面向對象編程基礎

6 訪問控制

1 私有屬性

使用雙下劃線開頭的屬性名,就是私有屬性
通過在類的構造時使用self.__x=x 來 獲取類的私有屬性。

默認的類的私有屬性不能訪問:
面向對象編程基礎
通過將其私有屬性包裝成方法進行使用,其是可以訪問的。
面向對象編程基礎

實例如下:

#!/usr/bin/poython3.6
#conding:utf-8
class Person:
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def  char(self):
        if   0< self.age  < 100:
            return  self.age

tom=Person('tom',50)
print (tom.char())
jerry=Person('jerry',110)
print (jerry.char())  # 此處的返回為空,因為沒有滿足的條件
print (jerry.age)  # 可通過此種方式進行返回,因此其是無法真實控制的

結果如下
面向對象編程基礎

此處python提供了私有屬性用于解決此種問題

#!/usr/bin/poython3.6
#conding:utf-8
class Person:
    def __init__(self,name,age):
        self.name=name
        self.__age=age  # 私有屬性,隱藏之后無法使用實例名.__age進行調用,必須將其抽象成一個方法并輸出的方式進行處理

    def  char(self):
        if   0< self.__age  < 100:
            return  self.__age #
    def  charget(self):
        return  self.__age
tom=Person('tom',50)
print (tom.char())
jerry=Person('jerry',110)
# print (jerry.__age)  #此處無法通過__age的方法進行調用嗎
print  (jerry.charget()) #此處可有方法進行調用處理
print  (jerry.__dict__) # 事實上,此處的私有屬性在python中是修改了名稱,其結果是實例名._類名稱__age可進行調用,具體可參見__dict__獲取,此處私有屬性是在實例字典中存在的
print (jerry._Person__age)
jerry._Person__age=300  #修改私有屬性參數值,但其測試,顯示中既然是私有屬性,則不建議修改其屬性值
print (jerry._Person__age)  # 修改結果查看

結果如下
面向對象編程基礎

私有變量的本質: 類定義的時候,如果聲明一個實例變量的時候,使用雙下劃線,python解釋器會將其改名,轉換成為實例名._類名__變量名,所以原來的名字訪問不到了,私有屬性是在實例的字典中的

2 保護變量

在變量名前使用一個下劃線,稱為保護變量

#!/usr/bin/poython3.6
#conding:utf-8
class Person:
    def __init__(self,name):
        self._name=name   # 保護變量,python開發人員約定俗稱的,不可修改的,但實際其是可以被實例調用且修改的,但建議其使用方法進行封裝處理
    def  printf(self):
        return  self._name
tom=Person('tom')
print  (tom._name)
tom._name='jerry'
print  (tom.printf())  #修改后打印

結果如下
面向對象編程基礎

可以看出,這個_name屬性根本就沒改變名稱,和普通的屬性一樣,解釋器不做任何特殊處理,這只是開發者共同的約定,看見這種變量,就如同私有變量,不要直接使用,保護變量也是在實例的字典中的。

3 私有方法

#!/usr/bin/poython3.6
#conding:utf-8
class Person:
    def __init__(self,name):
        self.name=name
    def  __printf(self): #私有方法,其本質是對其名稱做了修改,其方法是類的方法是Person.__printf
        return  self.name
tom=Person('tom')
print (Person.__dict__)
# print  (tom.__printf())  #修改后打印
print (tom.__dict__) # 實例列表中無此方法
print  (tom._Person__printf())  # 此處的調用先找到實例,然后沒有這方法,然后找到類,找到該方法,

結果如下
面向對象編程基礎

在函數上的雙下劃線,是屬于類的屬性,方法內部(函數)的雙下劃線,是屬于實例的屬性,而不是類的屬性
私有方法的本質
單下劃線的方法只是開發者之間的約定,解釋器不會做任何改變
雙下劃線的方法,是私有方法,解釋器會改名,改變策略和私有變量相同,但其私有變量是在實例字典中,而私有方法卻是在類字典中

4 總結

私有成員總結
在python中使用單下劃線或者雙下劃綫來標識一個成員被保護或者私有化隱藏起來,但是,不管使用什么樣的方式訪問控制,都不能真正的阻止用戶修改類的成員屬性,python中沒有絕對安全的保護成員或者私有成員

因此,前面的下劃線是一種警告或者提醒,請遵守這個約定,除非真的必要,否則不要修改隨意使用成員保護或者私有成員。

7 屬性裝飾器

屬性裝飾器的目的: 把實例的屬性保護起來,不讓外部直接訪問,外部使用getter讀取屬性和setter方法設置屬性。

一般方法

#!/usr/local/bin/python3.6
#coding:utf-8
class  Person:
    def  __init__(self,name,age=18):
        self.__name=name
        self.age=age
    def  getname(self):  #查看名稱的屬性
        return   self.__name
    def setname(self,name):  # 修改名稱屬性
        self.__name=name

tom=Person('tom')
print (tom.getname())
tom.setname('jerry')
print (tom.getname())

結果如下
面向對象編程基礎

屬性裝飾器

#!/usr/local/bin/python3.6
#coding:utf-8
class  Person:
    def  __init__(self,name,age=18):
        self.__name=name
        self.age=age
    @property
    def  getname(self):  #查看名稱的屬性
        return   self.__name
    @getname.setter  #修改屬性值,此處的getname和上面的和下面的必須相同,否則會出現錯誤
    def  getname(self,value):
        self.__name=value
    @getname.deleter  # 刪除屬性值,使用del  self.__name 進行對屬性的刪除操作
    def getname(self):
        #del  self.__name
        print  ('delete self.__name')
tom=Person('tom')
print (tom.getname)
tom.getname='jerry'  # 修改屬性值
print (tom.getname)
del tom.getname  #外部調用執行刪除程序

結果如下

面向對象編程基礎

第二種寫法

#!/usr/bin/poython3.6
#conding:utf-8
class  Person:
    def  __init__(self,chinese,english,history):
        self._chinese=chinese
        self.__english=english
    def  seteng(self,value): # 只有getter屬性,此中成為只讀屬性
        self.__english=value
#第二種寫方式,初始化方法
    eng=property(lambda   self:self.__english,seteng ) # property 是只讀的,此處的傳入的值,eng是一個函數,第一個是gettter,第二個是setter

stu=Person(90,100,110)
print (stu.eng)
stu.eng=200  #修改其值
print (stu.eng)

結果如下

面向對象編程基礎

通過此裝飾器@property: 定義一個類方法為私有屬性的名稱;讓用戶可以直接訪問, 但不能任意修改;

面向對象編程基礎

通過其裝飾器的方法進行重新定義一個接受隱藏屬性的范圍來進行修改其值。

@屬性名.seeter: 給屬性賦值時先做判斷; 當屬性名=value會自動調用該函數

通過deleter 方法可以使得當del 屬性名, 會自動調用該函數并執行高函數下面的操作~~

面向對象編程基礎

特別注意: 這三個方法同名

property 裝飾器
后面跟的函數名就是以后的屬性名,它就是getter,這個必須有,有了它至少是只讀屬性

setter裝飾器
與屬性名同名,且接受2個參數,第一個是self,第二個是將要賦值的值,有了它,屬性可寫

deleter 裝飾器
可以控制是否刪除屬性,很少用

property裝飾器必須在前,setter、deleterious裝飾器在后

property 裝飾器能通過簡單的方式,把對象方法的操作編程對屬性的訪問,并起到了一定的隱藏作用


@property應用:
用于分頁顯示,此處傳入兩個值,一個值是第幾頁,另一個是每頁的數量,通過@property 可以進行對列表的操作。

面向對象編程基礎

8 對象的銷毀

類中可以定義del方法,稱為析構函數(方法)
作用: 銷毀類的實例的時候調用,用以釋放占用的資源 ,其目標是銷毀實例

由于python實現了垃圾回收機制,這個方法不能確定何時使用,有必要的時候,請使用del語句刪除實例

#!/usr/bin/poython3.6
#conding:utf-8
class  Person:
    def  __init__(self,name,age=20):
        self.name=name
        self.__age=age
    def  __del__(self):  # 設置消亡實例
        print ("delete instance {}".format(self.name))

a=Person('tom')
del a # 調用消亡實例

結果如下
面向對象編程基礎

9 方法重載 (overload)

在其他面向對象的高級語言中,都有重載的概念,所謂的重載,就是同一個方法名,但是參數數量,類型不一樣,就是同一個方法的重載

python 沒有重載,python不需要重載
python中,方法(函數)定義中,形參非常靈活,不需要指定類型,參數個數也不固定,一個函數的定義可以實現多種不同的形式,因此python不需要重載。
python中只要標識符相同,則會被完全覆蓋,

10 練習

1 隨機數生成類,可以指定一批生成的個數,可以指定數值的范圍,生成隨機數

#!/usr/bin/poython3.6
#conding:utf-8
import  random
class  randsum:
    @classmethod
    def  getsrandsum(cls,min=1,max=10,num=10):
        return  [random.randint(min,max)  for _ in range(num)]

print  (randsum.getsrandsum())
print (randsum.getsrandsum(10,20,5))

結果如下:

面向對象編程基礎

2 隨機生成隨機數,并兩兩配對形成二維坐標系,把這些坐標組織起來,并進行打印

#!/usr/bin/poython3.6
#conding:utf-8
import random
class  RandomNum:
    def  __init__(self,count=10,min=1,max=10):
        self.count=count
        self.min=min
        self.max=max
        self.g=self._generate()  # 此處可使用此種方式將內部的方法以屬性的形式進行處理,供內部調用
    def  _generate(self):
        while True:
            yield  [random.randint(self.min,self.max)  for _ in range(self.count)]  #c此處返回一個列表生成式,用于下面的next()進行調用
    def genrate(self,count):
        self.count=count
        return   next(self.g)  # 此處通過此種方式進行處理,返回值為隨機數的單個個體

a=RandomNum()
for k,v in  dict(zip(a.genrate(5),a.genrate(5))).items():
    print ("(x={},y={})".format(k,v))

結果如下
面向對象編程基礎

3 記錄車的品牌mark,顏色color,價格 price,速度等特征,并實現增加車輛信息,顯示全部車輛信息的功能。

#!/usr/bin/poython3.6
#conding:utf-8
class  Car:
    def  __init__(self,mark,speed,color,price):
        self.mark=mark
        self.speed=speed
        self.color=color
        self.price=price

class  CarInfo:  # 信息本身也是一個對象,一個容器的對象
    def  __init__(self):
        self.lst = []
    def addcar(self,car:Car):  #此處可調用上述的類
        self.lst.append(car)
    def getall(self):
        return  self.lst  # 打印的格式化輸出

ci=CarInfo()# 此處完成后,其基本的列表創建已經完成
car=Car('audi',100,'red',1500000)  # 此處是初始化 
ci.addcar(car)  # 調用此初始化數據并增加
print  (ci.getall())  # 查看

四 python 補丁

1 概述

可以通過修改或者替換成員,使用者調用方沒改變,但是,類提供的功能可能已經修改了,當項目需要修改較多的類時,又著急上線,修改起來比較麻煩時,需要通過在一個文件中指定某個類進行相關的操作來對其進行打補丁
猴子補丁
運行時,對屬性進行動態替換
黑魔法,慎用

2 具體使用如下

test 源文件

#!/usr/bin/poython3.6
#conding:utf-8
class  Person:
    def  __init__(self,chinese,english,history):
        self.chinese=chinese
        self.english=english
        self.history=history
    def  getscore(self):
        return  (self.history,self.english,self.chinese)

a=Person(70,80,90)
print (a.getscore())

默認結果如下
面向對象編程基礎
補丁文件

test1

#!/usr/bin/poython3.6
#conding:utf-8

def  getscore(self):  # 其名稱和相關的類對象名必須一致,否則會報錯,此處返回是一個字典
    return  dict(chi=self.chinese,eng=self.english,his=self.history)

test2

#!/usr/bin/poython3.6
#conding:utf-8
from  test import  Person
from  test1 import getscore
def  monkeypatch5Persoon():
    Person.getscore=getscore  # 對類來改變屬性,在類的DICT中有方法,
student = Person(80,91,80)

print (student.getscore())
monkeypatch5Persoon()
student1 = Person(80,91,80)
print (student1.getscore())

結果如下
面向對象編程基礎

五 面向對象的三大特性之封裝

1 概念

組裝: 將數據和操作組裝到一起
隱藏數據:對外只暴露一些接口,通過接口訪問對象
封裝實際上是把數據封裝到摸個地方,以后再去調用在某處的內容或者數據
調用封裝數據的方式
通過對象直接調用
其中init表示的是一個構造器,當類的實例化過程時會調用其方法,由于其是必選參數,因此在傳值時必須與對應的個數相同,當然可以實例化多個對象

2 相關實例

面向對象編程基礎
面向對象編程基礎
通過self在類內部對類中的數據進行調用
面向對象編程基礎
類的實例化
面向對象編程基礎
面向對象編程基礎

六面向對象的三大特性之繼承(inheritance)

1 概念

多復用,繼承來的就不用自己寫了
多繼承少修改,OCP,使用繼承來改變,來提現個性
基類和派生類
其中父類也叫基類
子類也叫派生類

父類

面向對象編程基礎

子類,其中沒有定義類的屬性和方法,只是繼承了Class1 的屬性

面向對象編程基礎

子類的實例化和結果,其完全繼承和父類的屬性和方法:

面向對象編程基礎

當子類中有自己的構造函數時,以子類中的構造函數為準

面向對象編程基礎
面向對象編程基礎

2 基本實踐

1 共有屬性和方法繼承

#!/usr/bin/poython3.6
#conding:utf-8
class  A:
    x=123
    def __init__(self,name):
        self.__name=name
    @property
    def name(self): # 此處的A使用裝飾器將方法封裝成屬性并進行暴露
        return self.__name
    def shout(self):
        print ('A  shout')
class B(A):  #此處的B 繼承了A的屬性
    x=456
    def shout(self):  # 此處的B對A的方法進行了重寫
        print ('B shout')

class C(B):
    pass

tom=C('tom')
print (tom.name)
tom.shout()

結果如下

面向對象編程基礎

2 私有屬性的繼承

默認的,私有屬性不能直接調用,需要偽裝成方法方可調用。

面向對象編程基礎

#!/usr/bin/poython3.6
#conding:utf-8
class A:
    x=123
    def __init__(self,name='tom',age=10):
        self.name=name
        self.__age=age
    def shout(self):
        print ('A shount')
    @property
    def getage(self):
        return  self.__age
class B(A):
    x=456
    def shout(self):
        print ('B shout')

jerry=A('tom')
print (jerry.__dict__)
tom=B('jerry')
print (tom.__dict__) # 默認的A的私有屬性會被B讀取,并可進行訪問,其方法亦可被訪問
print (tom.getage)

結果如下
面向對象編程基礎

3 私有方法繼承

在類中,以雙下劃綫開始的方法稱為私有方法:私有方法不能繼承。

面向對象編程基礎

#!/usr/bin/poython3.6
#conding:utf-8
class  A:
    def  __init__(self,name,age):
        self.name=name
        self.age=age
    def  __getname(self): #此處定義私有方法
        return  self.name

class B(A):
    pass

b=B('tom',30)  
print (b.__dict__)
print  (B.__dict__)
a=A('jerry',20)
print (a.__dict__)
print (A.__dict__)  # 私有方法是屬于類的,不能被繼承,

結果如下:
面向對象編程基礎

3 查看繼承的特殊屬性和方法

1 基本含義

特殊屬性和方法 含義
_base_ 類的基類
_bases_ 類的基類元組
_mro_ 顯示方法查找順序,基類的元組
mor() 同上
_subclasses_() 類的子類列表

2 基本實例

#!/usr/bin/poython3.6
#conding:utf-8
class  A:
    x=123
    def __init__(self,name):
        self.__name=name
    @property
    def name(self): # 此處的A使用裝飾器將方法封裝成屬性并進行暴露
        return self.__name
    def shout(self):
        print ('A  shout')
class B(A):  #此處的B 繼承了A的屬性
    x=456
    def shout(self):  # 此處的B對A的方法進行了重寫
        print ('B shout')

class C(B):
    pass
print (C.__base__)  #顯示此類的基類
print (C.__bases__) # 顯示此類的基類,以元組呈現
print (C.__mro__)  # 顯示類的鏈表,由此類的父類開始查找
print (C.mro())  # 通上
print (A.__subclasses__())  # 顯示此類的子類

結果如下
面向對象編程基礎

類和實例屬性列表打印

#!/usr/bin/poython3.6
#conding:utf-8
class  A:
    x=123
    def __init__(self,name):
        self.__name=name
    @property
    def name(self): # 此處的A使用裝飾器將方法封裝成屬性并進行暴露
        return self.__name
    def shout(self):
        print ('A  shout')
class B(A):  #此處的B 繼承了A的屬性
    x=456
    def shout(self):  # 此處的B對A的方法進行了重寫
        print ('B shout')

class C(B):
    pass
tom=C('tom')
print (tom.__dict__)  # 打印實例的字典,其中只有實例化的屬性的列表
print (C.__dict__)  # 打印C類的字典,C類無任何屬性和方法,因為其是來自于B和A的
print (B.__dict__) # 打印B類的字典,其中有x類屬性的定義,shout函數的定義
print (A.__dict__)# A中包括初始化函數等信息

如下
面向對象編程基礎

3 結論

類和類屬性只有一個,而實例和實例屬性卻有多個
凡是需要分成多個的,應該設計成類的實例屬性,凡是需要一份的,應該設計成類的屬性
一個子類繼承了祖先的特征,但祖先的都是抽象的東西,只有對象才具有繼承數據的能力,對于實例,不同的需要設置各個單獨的屬性,需要放置在自己的屬性列表中,如果需要一份,則放置在類屬性上。

4 重寫父類的構造函數

1 python 2.0 中的寫法

A.init(self,args)

1 通過 父類名.init(self,父類參數) 進行重寫構造函數

面向對象編程基礎

2 通過super對私有方法重寫

通過super(自己類名稱,self).init(形參)

優點: 不需要明確告訴父類的名稱,如果父類改變,只需修改class 語句后面的繼承關系即可,不用修改父類名稱

面向對象編程基礎
實例化調用
面向對象編程基礎

#!/usr/bin/poython3.6
#conding:utf-8
class  A:
    x=123
    def shout(self):
        print ('A shout')

class B(A):
    def shout(self):  #此處定義覆蓋了父類的shout
        print ('B')
    def shout(self): # 此處定義覆蓋了自身的shout
        print (super()) # 顯示super內容
        print (super(B,self))# 此處相當于將B設置為self傳輸進去。等價與上面的super
        super().shout()#此處調用的是父類并調用了父類的shout
a=B()
a.shout()

結果如下
面向對象編程基礎

#!/usr/bin/poython3.6
#conding:utf-8
class  A:
    x=123
    def __init__(self,name='tom',age=0):
        self.__name=name # 此處是實例的初始化函數,當然要在其實例化后才能表現出該屬性,該類中也找不到,當然在其子類中也不能找到
        self.age=age
    @property
    def  getname(self):
        return  self.__name
    def shout(self):
        print ('A shout')

class B(A):
    x=456
    def  __init__(self,name,age):
        self.age=age # 此處的傳入后會修改成50,因為其是在修改之后傳入A類中進行處理的
        super().__init__(name,age) #新式類推薦使用的方式,此處默認傳入self,需要送入指定參數,此處不許找指定self和父類的類名
        self.__name=name+" "+'B' # 私有屬性的修改是無法生效的,因為其私有屬性的key和類名相關,
        # self.age=10  #覆蓋共有屬性,其之前的age是0,現在覆蓋成10,因為實例的字典中的key是唯一的。此處因為在a.__init__(self,name)之后定義,因此其會
        # 覆蓋之前name傳入的值
    def shout(self):
        print ('B shout')
        super(B,self).shout()  #此處是調用父類的,通過super可以方面的訪問自己的祖先類,其查找順序是找本實例,若通過本實例查找
        #其類,通過類查找其父類并進行相關的操作
        super().shout() # 與上述相同
x=B('jerry',30) # 此處傳入的值是30,由于下面的覆蓋,變成了10
x.shout()
print (x.__dict__)  #其私有屬性的_A__name 仍然存在,_B__name 也存在,
print (x.getname)

結果如下

面向對象編程基礎

5 公有屬性繼承中的初始化

1 公有屬性覆蓋和父類公有屬性調用

#!/usr/bin/poython3.6
#conding:utf-8
class A:
    x=123
    def __init__(self,name='tom',age=10):
        self.name=name
        self.age=age
    def shout(self):
        print ('A shount')
class B(A):
    x=456
    def  __init__(self,name):  #此處進行初始化,初始化后,其父類的公有屬性將不能被調用
        self.name=name #此處重新覆蓋了共有屬性name,由之前的tom修改成了jerry
    def shout(self):
        print ('B shout')
tom=B('jerry')
print (tom.name)
print (tom.__dict__)
#print  (tom.age) # 此處是父類的屬性,此處不能被調用

結果如下

面向對象編程基礎

2 公有屬性重命名

#!/usr/bin/poython3.6
#conding:utf-8
class A:
    x=123
    def __init__(self,name='tom',age=10):
        self.name=name
        self.age=age
    def shout(self):
        print ('A shount')
class B(A):
    x=456
    def  __init__(self,name):  #此處進行初始化,初始化后,其父類的公有屬性將不能被調用
        self.cname=name #此處定義了公有屬性cname
    def shout(self):
        print ('B shout')
tom=B('jerry')
print (tom.cname)
# print (tom.name)  #此處是父類的屬性,不能被調用,因為子類進行了初始化
print (tom.__dict__) # 此處只有cname
#print  (tom.age) # 此處是父類的屬性,此處不能被調用

面向對象編程基礎

3 調用父類A.init(self,args) 來獲取父類的共有屬性

#!/usr/bin/poython3.6
#conding:utf-8
class A:
    x=123
    def __init__(self,name='tom'):
        self.name=name
    def shout(self):
        print ('A shount')
class B(A):
    x=456
    def  __init__(self,name):  #此處進行初始化,此處需要調用父類的方法,若不調用,則默認父類的私有方法不能使用,但共有方法可以被調用
        self.cname=name #此處重新定義了共有屬性cname,
        A.__init__(self,name) # 此處調用了父類的name
    def shout(self):
        print ('B shout')
tom=B('jerry')
print (tom.name) #此處name存在于A類中。而B中調用A.__init__(self,args) 導致其A中存在的屬性在B中也同樣存在
print (tom.cname)
print (tom.__dict__)# 其共有方法中的name存在,也有自己的初始化的cname

結果如下

面向對象編程基礎

4 共有屬性中順序不同導致的問題

#!/usr/bin/poython3.6
#conding:utf-8
class  A:
    x=123
    def __init__(self,name='tom',age=0):
        self.__name=name
        self.age=age
    @property
    def  getname(self):
        return  self.__name
    def shout(self):
        print ('A shout')

class B(A):
    x=456
    def  __init__(self,name,age):
        #self.age=age # 此處的傳入后會修改成30,因為其是在修改之后傳入A類中進行處理的
        A.__init__(self,name,age)  #通過父類的調用來調用父類的屬性,此中可調用父類的私有屬性,
        self.__name=name+" "+'B' # 私有屬性的修改是無法生效的,因為其私有屬性的key和類名相關,
        self.age=10  #覆蓋共有屬性,其之前的age是0,現在覆蓋成10,因為實例的字典中的key是唯一的。此處因為在a.__init__(self,name)之后定義,因此其會
        # 覆蓋之前name傳入的值
    def shout(self):
        print ('B shout')

x=B('jerry',30) # 此處傳入的值是30,由于下面的覆蓋,變成了10
x.shout()
print (x.__dict__)  #其私有屬性的_A__name 仍然存在,_B__name 也存在,
print (x.getname)

結果如下
面向對象編程基礎

#!/usr/bin/poython3.6
#conding:utf-8
class  A:
    x=123
    def __init__(self,name='tom',age=0):
        self.__name=name # 此處是實例的初始化函數,當然要在其實例化后才能表現出該屬性,該類中也找不到,當然在其子類中也不能找到
        self.age=age
    @property
    def  getname(self):
        return  self.__name
    def shout(self):
        print ('A shout')

class B(A):
    x=456
    def  __init__(self,name,age):
        self.age=age # 此處的傳入后會修改成50,因為其是在修改之后傳入A類中進行處理的
        A.__init__(self,name,age)  #通過父類的調用來調用父類的屬性,此中可調用父類的私有屬性,
        self.__name=name+" "+'B' # 私有屬性的修改是無法生效的,因為其私有屬性的key和類名相關,
        # self.age=10  #覆蓋共有屬性,其之前的age是0,現在覆蓋成10,因為實例的字典中的key是唯一的。此處因為在a.__init__(self,name)之后定義,因此其會
        # 覆蓋之前name傳入的值
    def shout(self):
        print ('B shout')

x=B('jerry',30) # 此處傳入的值是30,由于下面的覆蓋,變成了10
x.shout()
print (x.__dict__)  #其私有屬性的_A__name 仍然存在,_B__name 也存在,
print (x.getname)

結果如下
面向對象編程基礎

5 私有屬性中繼承中的初始化

1 私有屬性未處理和調用

#!/usr/bin/poython3.6
#conding:utf-8
class A:
    x=123
    def __init__(self,name='tom',age=10):
        self.name=name
        self.__age=age
    def shout(self):
        print ('A shount')
    @property
    def getage(self):
        return  self.__age
class B(A):
    x=456
    def  __init__(self,name,age): # 子類的初始化
        self.name=name # 此處進行覆蓋共有屬性,其私有屬性未處理
    def shout(self):
        print ('B shout')

b=B('jerry',30)
print (b.__dict__)  # 此處只獲取到了重新定義的name,而私有屬性__age 不存在

2 私有屬性覆蓋

#!/usr/bin/poython3.6
#conding:utf-8
class A:
    x=123
    def __init__(self,name='tom',age=10):
        self.name=name
        self.__age=age
    def shout(self):
        print ('A shount')
    @property
    def getage(self):
        return  self.__age
class B(A):
    x=456
    def  __init__(self,name,age): # 子類的初始化
        self.__age=age # 此處進行覆蓋私有屬性,其私有屬性未處理
    def shout(self):
        print ('B shout')

b=B('jerry',30)
print (b.__dict__)  # 此處只獲取到了重新定義的__age,而共有屬性name則不存在

結果如下

面向對象編程基礎

3 調用父類方法獲取共有屬性和私有屬性列表

#!/usr/bin/poython3.6
#conding:utf-8
class A:
    x=123
    def __init__(self,name='tom',age=10):
        self.name=name
        self.__age=age
    def shout(self):
        print ('A shount')
    @property
    def getage(self):
        return  self.__age
class B(A):
    x=456
    def  __init__(self,name,age): # 子類的初始化
        A.__init__(self,name,age)  #調用父類方法,處理獲取屬性
    def shout(self):
        print ('B shout')

b=B('jerry',30)
print (b.__dict__)  # 此處可獲取父類中的共有屬性和私有屬性
print  (b.name,b._A__age)  # 已經進行了修改,,因為上面的def  __init__(self,name,age): 中的age在實例化后已經被修改
print (b.getage)

結果如下
面向對象編程基礎

4 私有屬性之重新初始化

#!/usr/bin/poython3.6
#conding:utf-8
class A:
    x=123
    def __init__(self,name='tom',age=10):
        self.name=name
        self.__age=age
    def shout(self):
        print ('A shount')
    @property
    def getage(self):
        return  self.__age
class B(A):
    x=456
    def  __init__(self,name,age): # 子類的初始化
        A.__init__(self,name,age)  #調用父類方法,處理獲取屬性
        self.__age=age # 重覆蓋age,但無法完成,因為其私有屬性和類名有關,其相當于增加
    def shout(self):
        print ('B shout')

b=B('jerry',30)
print (b.__dict__)  # 此處可獲取父類中的共有屬性和私有屬性
print  (b.name,b._A__age)  # 已經進行了修改,,因為上面的def  __init__(self,name,age): 中的age在實例化后已經被修改
print (b.getage)

結果如下
面向對象編程基礎

6 類方法和靜態方法繼承中的初始化

1 實例

#!/usr/bin/poython3.6
#conding:utf-8
class  A:
    @classmethod
    def class_method(cls):
        print (cls.__name__)  # 此處打印類的名稱
    @staticmethod  # 此處定義其靜態方法
    def static_method():
        print ('A staticmethod')

class B(A):
    @classmethod
    def class_method(cls):
        print (cls.__name__)
    @staticmethod
    def static_method():
        print ('B staticmethod')

class  C(B):
    pass
b=C()
b.class_method()  # 此處因為調用的是C類,因此其cls.__name__打印的是C而并非是B
b.static_method()  # 此處的靜態方法是B,因為C中未定義靜態方法
print (B.__dict__)

a=A()
a.class_method()  # 此處因為調用的時A類,因此cls.__name__打印的是A
a.static_method()
print (A.__dict__)

結果如下
面向對象編程基礎

2 結論

靜態方法會進行屬性查找,找到及截至,類方法因為類的繼承而進入該C類中,而C類調用其類方法后得到的名稱當然是C自己,此處若只有A配置類屬性,則B和C進行調用,則各自cls.name 仍然是各個實例的類,相當于自己在調用自己的類。此處大大的復用了,此處就是多態

7 私有方法繼承中的初始化

1 私有方法重寫

#!/usr/bin/poython3.6
#conding:utf-8
class  A:
    def  __init__(self,name,age):
        self.name=name
        self.age=age
    def  __getname(self): #此處定義私有方法
        return  self.name

class B(A):
    def __init__(self,name,age): # 此處進行初始化,方法不能繼承
        self.name=name
    def __getname(self):  # 此處屬于重定義私有方法
        return self.age

b=B('tom',30)
print (b.__dict__)
print  (B.__dict__)
a=A('jerry',20)
print (a.__dict__)
print (A.__dict__)  # 私有方法是屬于類的,不能被繼承,

結果如下

面向對象編程基礎

8 多繼承

1 多重繼承簡介

原則: OCP 原則,多繼承,少修改
繼承的用途: 增強基類,實現多態

多態:在面向對象環境中,父類,子類通過繼承聯系到一起,如果可以通過一套方法,就可以實現不同表現,這就是多態

一個類繼承多個類就是多繼承,它具有多個類的特征

多態是繼承+多態


多態的弊端:
1 多繼承很好的模擬了世界,因為事物很少是單一繼承,但是舍棄簡單,必然引入復雜性,帶來了沖突
多繼承的實現會導致編譯器設計的復雜度增加,所以很多語言都舍棄了類的多繼承。

多繼承可能帶來二義性
解決方案
實現多繼承的語言,需要解決二義性和,深度優先或廣度優先

面向對象編程基礎

左邊是多繼承,右邊是單繼承

多繼承帶來的路徑選擇問題
python 使用MRO(method resolution order)解決了基類搜索順序問題


歷史原因,MRO有三個搜索算法:
1 經典算法,按照定義從左到右,深度優先策略,2.2之前
左圖的MRO是MyClass,D,B,A,C,A
新式類算法,經典算法的升級,重復的只保留最后一個2.2
左圖的MRO是MyClass,D,B,C,A,object
C3 算法,在類被創建出來的時候,就計算出一個MRO有序列表,2.3之后,python3唯一支持的算法
左圖中的MRO是MyClass,D,B,C,A,object 的列表
C3 算法解決的多繼承的二義性


多繼承的缺點

當類很多時,繼承復雜的情況下,繼承路徑太多,很難說清什么樣的繼承路徑

Python 語法是允許多繼承,但python代碼是解釋器執行的,只有執行到的時候,才發現錯誤

團隊協作開發,如果引入多繼承,代碼將不可控

不管編程語言是否支持多繼承,都應當避免多繼承
python2.x 里面支持經典類和新式類
python3.x 里面僅支持新式類
經典類,其可以不寫父類。

面向對象編程基礎

新式類,其如果沒有繼承的父類則直接寫object,必須寫父類,如果有父類,則直接寫父類

面向對象編程基礎

只有新式類支持mro() 方法

面向對象編程基礎

對于新式類,是廣度優先
對于經典類,是深度優先

面向對象編程基礎

對于新式類,當調用D實例時,其結果是執行D的輸出,當D中為pass 占位,其繼承了B的輸出,當B中的結果為pass 時,其會繼承C中的輸出,

對于經典類,當調用D實例時,其結果是執行D的輸出,當D中為pass 占位,其繼承了B的輸出,當B中的結果為pass 時,其會繼承A中的輸出,因為B繼承了A,因為其是深度優先。

9 總結

繼承時,共有的,子類和實例都可以隨意訪問,私有成員被隱藏,子類和實例不可直接訪問,當私有變量所在的類內的方法中可以訪問這個私有變量,python通過自己一套實現,實現和其他語言一樣的面向對象的繼承機制


屬性查找順序
實例的_dict_-----類的_dict_ -----父類的_dict_,如果搜索到這些地方后沒有找到就會拋異常,先找到就立即返回了。

七 Mixin

1 簡介

裝飾器: 用裝飾器裝飾一個類,把功能給類附加上去,那個類需要,就裝飾它

Mixin
面向對象編程基礎

文檔Document 類是其他所有文檔類的抽象基類,
Word,PDF類是Document的子類。
此處可抽象為兩種類
1 共同的特性抽象成一個類,父類
2 其他不同的特性組成其他的相關的屬性,子類


Mxin 本質上是多繼承實現的
mixin 提現的是一種組合的設計模式
在面向對象的設計中,一個復雜的類,往往需要很多功能,而這些功能有來自不同的類提供,這些就需要很多組合在一起。


MIxin 類的使用原則:
Mixin 類中不應該顯式的出現__init__初始化方法
Mixin 類通常不能獨立工作,因為它是準本混入別的類中的部分功能實現
Mixin 類的祖先類也應該是Mixin類


Mixin類和裝飾器

這兩種方式都是可以使用,看個人喜好
如果需要繼承就使用MixIn類的方法

2 裝飾器處理代碼繼承問題如下

1 子類中欲實現自己的功能自己處理,不再父類中進行定義

#!/usr/local/bin/python3.6
#coding:utf-8
class  Document:  # 父類
    def __init__(self,content):
        self.content=content

    def  print(self):  # 共有的屬性
        print (self.content)
class Word(Document):
    pass  
class PrintableWord(Word):
    def print(self): # 子類自己的私有屬性
        print ("Word pinrt {}".format(self.content))
class  Pdf(Document):
    pass  # 子類自定義私有屬性
print (PrintableWord.mro())
word=PrintableWord('test\nabc')
word.print()

結果如下
面向對象編程基礎

2 通過父類中實現。子類若需要個性化需求,則自己修改

#!/usr/local/bin/python3.6
#coding:utf-8
def  printable(cls):  #此處傳入的是類函數
    def _print(self):  # 此處的實例可以看作是這個類函數的參數
        print (self.content,'裝飾器')  # 此處是計算的返回值
    cls.print=_print # 此處對返回值進行賦值
    return  cls  # 并返回該函數的執行結果
class Document:
    def __init__(self,content):
        self.content=content
class Word(Document):
    pass
class Pdf(Document):
    pass
@printable 
class  PrintableWord(Word):  #此處先繼承,后裝飾  
    pass
print  (PrintableWord.mro())
pw=PrintableWord('test  string')
pw.print()

結果如下

面向對象編程基礎

另一種方式,使用lambda 進行處理

#!/usr/local/bin/python3.6
#coding:utf-8
def  printable(cls):
    # 此處是給對應的類增加相關的屬性,使得其更加健壯
    cls.print=lambda   self: print (self.content)
    cls.NAME='name'
    return   cls
class Document:
    def __init__(self,content):
        self.content=content
class Word(Document):
    pass
class Pdf(Document):
    pass
@printable  #先繼承,后裝飾
class  PrintableWord(Word):
    pass
print  (PrintableWord.mro())
pw=PrintableWord('test  string')
pw.print()
print (pw.NAME)

結果如下
面向對象編程基礎

3 Mixin 方法處理

#!/usr/bin/poython3.6
#conding:utf-8

class Docment:
    def __init__(self,content):
        self.content=content
    def print(self):
        print (self.content)

class PrintableMixin:
    def print(self):
        print ('*'*20)
        print ('Pdf print {}'.format(self.content))
class   Word(Docment):
    pass

class  Pdf(Docment):
    pass
class PrintablePdf(PrintableMixin,Pdf): # 此處繼承了Mixin和Pdf兩個類,此處是多繼承,Mixin繼承了object。而Pdf繼承了Docment
    # 而Docment 繼承了object,此處的作用是使用PrintableMixin覆蓋之前的DOcment才是要求,一般的Mixin都是在最前面放的
    pass
print (PrintablePdf.mro()) #查看MRO的輸出,以MRO的輸出為準
Pdf=PrintablePdf('test\nabc')
Pdf.print()

結果如下
面向對象編程基礎

調換位置如下

#!/usr/bin/poython3.6
#conding:utf-8

class Docment:
    def __init__(self,content):
        self.content=content
    def print(self):
        print (self.content)

class PrintableMixin:
    def print(self):
        print ('*'*20)
        print ('Pdf print {}'.format(self.content))
class   Word(Docment):
    pass

class  Pdf(Docment):
    pass
class PrintablePdf(Pdf,PrintableMixin): # 此處繼承了Mixin和Pdf兩個類,此處是多繼承,Mixin繼承了object。而Pdf繼承了Docment
    # 而Docment 繼承了object,此處的作用是使用PrintableMixin覆蓋之前的DOcment才是要求,一般的Mixin都是在最前面放的
    pass
print (PrintablePdf.mro()) #查看MRO的輸出,以MRO的輸出為準
Pdf=PrintablePdf('test\nabc')
Pdf.print()

結果如下:
面向對象編程基礎

#!/usr/bin/poython3.6
#conding:utf-8

class Docment:
    def __init__(self,content):
        self.content=content
    def print(self):
        print (self.content)

class PrintableMixin:
    def print(self):
        print ('*'*20)
        print ('Pdf print {}'.format(self.content))

class   Word(Docment):
    pass

class  Pdf(Docment):
    pass

class  SuperPrintableMixin(PrintableMixin): # 此處使用類的繼承來打印一個超類
    def print(self):
        print ('~'*20)
        super().print()
        print('~'*20)
class PrintablePdf(SuperPrintableMixin,Pdf): 
    pass
print (PrintablePdf.mro()) #查看MRO的輸出,以MRO的輸出為準
Pdf=PrintablePdf('test\nabc')
Pdf.print()

結果如下

面向對象編程基礎

4 結論如下:

裝飾器使用的是函數的方式實現的,其函數只能實現封裝功能,而Mixin是一個類,其類可實現封裝,繼承,和多態的特性。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

沈阳市| 云南省| 阿尔山市| 通州市| 长垣县| 福清市| 洪泽县| 军事| 景德镇市| 大洼县| 元朗区| 宝鸡市| 建瓯市| 平山县| 宣城市| 新郑市| 蒙自县| 西青区| 温州市| 临湘市| 凭祥市| 祥云县| 思南县| 普宁市| 昭平县| 辉县市| 桦南县| 榆社县| 彰化市| 平塘县| 横峰县| 浮山县| 泾川县| 汶上县| 西乌珠穆沁旗| 襄垣县| 洛川县| 延津县| 多伦县| 陇川县| 临城县|