溫馨提示×

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

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

面向?qū)ο缶幊袒A(chǔ)

發(fā)布時(shí)間:2020-08-03 08:13:24 來源:網(wǎng)絡(luò) 閱讀:3413 作者:長(zhǎng)跑者1號(hào) 欄目:編程語(yǔ)言

一 編程的分類:

1 分類

1 面向過程編程
以指令為核心:圍繞“正在發(fā)生什么”進(jìn)行編寫
面向過程:程序= 算法+ 數(shù)據(jù)結(jié)構(gòu)
面向過程編程:程序具有一系列線性步驟,主體思想是代碼作用于數(shù)據(jù),以指令為核心,核心是設(shè)計(jì)算法,
2 面向函數(shù)的編程
3 面向?qū)ο蟮木幊?/p>

2 面向?qū)ο缶幊?/h3>

面向?qū)ο缶幊?-object oriented programming ,簡(jiǎn)稱OOP,把對(duì)象作為程序的基本單元,一個(gè)對(duì)象包含了數(shù)據(jù)和操作數(shù)據(jù)的函數(shù)
面向過程把函數(shù)繼續(xù)切分稱為子函數(shù),來降低系統(tǒng)的復(fù)雜度。
面向?qū)ο缶幊蹋∣OP)
程序 = 指令 + 數(shù)據(jù)/算法 + 數(shù)據(jù)結(jié)構(gòu)
以數(shù)據(jù)為核心: 圍繞“將影響誰(shuí)”進(jìn)行編寫
面向?qū)ο缶幊蹋∣OP):圍繞數(shù)據(jù)以及為數(shù)據(jù)嚴(yán)格定義的接口來組織程序,用數(shù)據(jù)控制對(duì)代碼的訪問,面向數(shù)據(jù),以及能夠?qū)@些數(shù)據(jù)采取的操作進(jìn)行,代碼執(zhí)行過成,不能跳過數(shù)據(jù)
面向?qū)ο缶幊痰暮诵母拍睿?br/>所有編程語(yǔ)言的最終目的都是提供一種抽象方法
在機(jī)器模型(“解空間”或“方案空間”)與實(shí)際解決問題的問題模型(“問題空間”)之間,程序員必須建立一種聯(lián)系
將問題空間中的元素以及他們?cè)诮饪臻g中的標(biāo)識(shí)物抽象為對(duì)象,并允許通過問題來描述問題而不是通過方案來描述問題
可以把實(shí)例(某個(gè)類的實(shí)例)想象成一種新型變量,他保存這數(shù)據(jù),但可以對(duì)自身數(shù)據(jù)執(zhí)行操作,實(shí)例可以對(duì)自身數(shù)據(jù)做操作

二 類和實(shí)例概念

1 類:

定義了被多個(gè)同一類型對(duì)象共享的結(jié)構(gòu)和行為(數(shù)據(jù)和代碼)
類是抽象的,實(shí)例是具體的,類是(概念,抽象的概念模型),類本身是不能被操作的,須有實(shí)例化的對(duì)象才能操作
類內(nèi)部有兩個(gè)核心成員:代碼和數(shù)據(jù),都叫類成員
數(shù)據(jù):成員變量或?qū)嵗兞?br/>成員方法: 簡(jiǎn)稱為方法,是操作數(shù)據(jù)的代碼,用于定義如何使用成員變量,因此一個(gè)類的行為和接口是通過方法來定義的
類:將同一種具體的物事的共同特性抽想出來的表現(xiàn)

2 實(shí)例:

實(shí)例是類的具體表現(xiàn)形式,類存在的目的就是為了實(shí)例化。類是抽象的,實(shí)例是具體的

三 類基本操作

1 類的定義

calss 類名(父類):
類的內(nèi)容
類后面有括號(hào)的稱為新式類,沒有括號(hào)稱為經(jīng)典類。
括號(hào)里面的內(nèi)容是父類的名稱,程序中,所有父類都是object

面向?qū)ο缶幊袒A(chǔ)
類的查看:
面向?qū)ο缶幊袒A(chǔ)

2 類的數(shù)據(jù)屬性(類變量)

面向?qū)ο缶幊袒A(chǔ)
當(dāng)類的實(shí)例化后,類的屬性是不變的
面向?qū)ο缶幊袒A(chǔ)

類的數(shù)據(jù)屬性是可變的,當(dāng)在實(shí)例處修改類的數(shù)據(jù)屬性時(shí),其類本身的數(shù)據(jù)屬性不變。

面向?qū)ο缶幊袒A(chǔ)

當(dāng)類的數(shù)據(jù)屬性發(fā)生改變時(shí),原本已經(jīng)改變的實(shí)例的數(shù)據(jù)屬性不會(huì)再次改變,而新創(chuàng)建的實(shí)例的數(shù)據(jù)屬性會(huì)發(fā)生改變

面向?qū)ο缶幊袒A(chǔ)

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

myclass=MyClass()  # 進(jìn)行實(shí)例化操作
print (myclass.x)  #調(diào)用類的屬性
print (MyClass.x) # 類調(diào)用類屬性
print (MyClass.foo(1))  # 類調(diào)用類方法
print (myclass.foo())  # 調(diào)用類的方法
print (type(myclass))  # 打印類型
print (id(MyClass))  # 打印類的實(shí)例地址
print (id(myclass))  # 打印類的內(nèi)存地址

結(jié)果如下
面向?qū)ο缶幊袒A(chǔ)

其實(shí)例化都的內(nèi)存地址和類本身的內(nèi)存地址不同,且實(shí)例可以調(diào)用類的屬性和方法,類自身也可以進(jìn)行相關(guān)的調(diào)用處理

3 類的方法===函數(shù)

在類中定義的函數(shù)教方法,類的方法中,要求第一個(gè)形參必須是self,而self實(shí)際上是類實(shí)例化后的對(duì)象本身
實(shí)例化后獲得的實(shí)例,是不同的實(shí)例,即使使用相同的參數(shù)實(shí)例化,也得到不一樣的對(duì)象,其會(huì)先調(diào)用new 進(jìn)行實(shí)例化,然后調(diào)用_init_ 進(jìn)行初始化
實(shí)例化后,會(huì)自動(dòng)調(diào)用__init__方法,這個(gè)方法的第一個(gè)參數(shù)必須留給self,其他參數(shù)隨意

類方法的定義
面向?qū)ο缶幊袒A(chǔ)
類方法的調(diào)用
面向?qū)ο缶幊袒A(chǔ)

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

結(jié)果如下

面向?qū)ο缶幊袒A(chǔ)

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

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

結(jié)果如下

面向?qū)ο缶幊袒A(chǔ)

#!/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):  # 通過此處的參數(shù),可修改類中的屬性
        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)  # 其返回結(jié)果為修改類屬性后的結(jié)果

結(jié)果如下
面向?qū)ο缶幊袒A(chǔ)

4 類的特殊屬性

1 簡(jiǎn)介

1 class._name_ 類的名稱顯示

面向?qū)ο缶幊袒A(chǔ)

2 class._doc_ 類的幫助文檔

3 class._base_ 類的父類/基類

面向?qū)ο缶幊袒A(chǔ)

4 class._module_ 類的模塊,當(dāng)不是導(dǎo)入的模塊的類時(shí),其執(zhí)行結(jié)果為main,當(dāng)為模塊導(dǎo)入時(shí),其執(zhí)行結(jié)果為模塊名

面向?qū)ο缶幊袒A(chǔ)

5 class._dict_ 對(duì)象屬性的字典,用于遍歷和查找屬性,每個(gè)對(duì)象中保存這自己的屬性,類的屬性在類字典中,實(shí)例的屬性在實(shí)例的字典中

6 class._class_ 對(duì)象的類型,__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):  # 通過此處的參數(shù),可修改類中的屬性
        self.name=x  # 修改name的屬性
        self.age=y
        Person.x=x  # 修改類屬性
        print   (self.name,self.age,x)
tom=Person('tom')
print  (tom.__class__,Person.__class__)  # 打印實(shí)例類型和類的類型
print (tom.__dict__,Person.__dict__)  # 打印實(shí)例的屬性字典和類的屬性字典
print  (tom.__qualname__,Person.__qualname__)  # 打印實(shí)例的限定名和類的限定名,默認(rèn)的,只有類有限定名

結(jié)果如下
面向?qū)ο缶幊袒A(chǔ)

如需輸出,則需要

#!/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):  # 通過此處的參數(shù),可修改類中的屬性
        self.name=x  # 修改name的屬性
        self.age=y
        Person.x=x  # 修改類屬性
        print   (self.name,self.age,x)
tom=Person('tom')
print  (tom.__class__.__qualname__)  #此處通過__class__吊起該實(shí)例的類,通過類調(diào)用其限定名即可
print  (isinstance(tom,Person)) #通過此處判斷該類是否是該實(shí)例的類
print(isinstance(tom,tom.__class__))

結(jié)果
面向?qū)ο缶幊袒A(chǔ)

#!/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):  # 通過此處的參數(shù),可修改類中的屬性
        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')# 打印實(shí)例和類的屬性字典

結(jié)果如下

面向?qū)ο缶幊袒A(chǔ)

#!/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')  #此處打印類和實(shí)例對(duì)象屬性字典
print (Person.age,tom.age,jerry.age)  # 結(jié)果為30,18,20

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

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

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

結(jié)果如下

面向?qū)ο缶幊袒A(chǔ)

#!/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調(diào)用對(duì)應(yīng)的value,此處不能調(diào)用height,因?yàn)槠洳辉趖om屬性字典中

結(jié)果如下
面向?qū)ο缶幊袒A(chǔ)

3 結(jié)論:

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

實(shí)例可以動(dòng)態(tài)的給自己增加一個(gè)屬性,實(shí)例.dict[變量名]和實(shí)例.變量名都可以訪問到

實(shí)例的同名變量會(huì)隱藏這類變量,或者說是覆蓋了這類變量

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

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

5 類方法和靜態(tài)方法

1 類方法

通過類方法@classmethod 進(jìn)行將類本身調(diào)用,其中cls 關(guān)鍵字就是表示類本身,
可以使用類名.類方法(參數(shù)) 的形式進(jìn)行調(diào)用。并使用return 返回結(jié)果

面向?qū)ο缶幊袒A(chǔ)

1 在定義類中,使用@classmethod 裝飾器修飾的方法
2 必須至少傳遞一個(gè)參數(shù),且第一個(gè)參數(shù)留給了cls,cls指代調(diào)用者及類對(duì)象自身
3 cls這個(gè)標(biāo)識(shí)符可以是任何合法名稱,但是為了易讀,請(qǐng)不要修改
4 通過cls可以直接操作類的屬性,其無法對(duì)實(shí)例的屬性進(jìn)行修改,因?yàn)槠涞谝粋€(gè)傳遞的是類對(duì)象,類中也不能調(diào)用實(shí)例的方法

相關(guān)實(shí)現(xiàn)

#!/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()

結(jié)果如下

面向?qū)ο缶幊袒A(chǔ)

實(shí)例如下

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

MyClass.my()  # 此處是類的自調(diào)用
a=MyClass() # 實(shí)例化,其類的相關(guān)屬性依然存在
a.my()

結(jié)果如下
面向?qū)ο缶幊袒A(chǔ)

2 靜態(tài)方法

1 在定義類中,使用@staticmethod 裝飾器裝飾的方法
2 調(diào)用時(shí),不會(huì)隱式傳入?yún)?shù)
3 靜態(tài)方法,只是表明這個(gè)方法屬于這個(gè)名稱空間,函數(shù)歸類在一起,方便管理組織

面向?qū)ο缶幊袒A(chǔ)

實(shí)例如下

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

總結(jié): 靜態(tài)方法和類方法都屬于類的屬性

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

datetime 是在類上調(diào)用的方法,進(jìn)而造出datetime對(duì)象而返回這個(gè)方法

python中常見的三種函數(shù):
1 和實(shí)例相關(guān)的函數(shù)
2 和類相關(guān)的函數(shù)
3 一般的函數(shù)

類的方法在類創(chuàng)建完成后即可進(jìn)行調(diào)用,而實(shí)例的方法在類創(chuàng)建完成之后不能調(diào)用

3 總結(jié):

類的方法調(diào)用
類幾乎可以調(diào)用所有內(nèi)部定義的方法,但是調(diào)用普通的方法時(shí)會(huì)報(bào)錯(cuò),原因是第一個(gè)參數(shù)必須是類的實(shí)例
實(shí)例幾乎可以調(diào)用所有的方法,普通的函數(shù)的調(diào)用一般不可能出現(xiàn),因?yàn)椴辉试S這么定義
類方法:默認(rèn)第一個(gè)參數(shù)是類本身,其可以被類調(diào)用,不能被實(shí)例調(diào)用
普通方法:默認(rèn)第一個(gè)參數(shù)是實(shí)例本身,可以被類和實(shí)例進(jìn)行調(diào)用
靜態(tài)方法:默認(rèn)第一個(gè)參數(shù)數(shù)傳入的參數(shù)。只能被類本身進(jìn)行調(diào)用,不能被實(shí)例調(diào)用


總結(jié):
類除了普通方法外都可以調(diào)用,普通方法需要對(duì)象的實(shí)例作為第一參數(shù)
實(shí)例可以調(diào)用所有類中的方法(包括類方法,靜態(tài)方法),普通方法傳入實(shí)例自身,靜態(tài)方法和類方法需要找到實(shí)例的類后進(jìn)行相關(guān)的處理。

4 在類中的應(yīng)用:

面向?qū)ο缶幊袒A(chǔ)
擴(kuò)展:
面向?qū)ο缶幊袒A(chǔ)

在類中調(diào)用類本身的方法:
傳統(tǒng)的輸入年、月、日、的方法:

面向?qū)ο缶幊袒A(chǔ)

通過函數(shù)進(jìn)行格式化的結(jié)果,通過在函數(shù)中調(diào)用類的方式實(shí)現(xiàn)

面向?qū)ο缶幊袒A(chǔ)

6 訪問控制

1 私有屬性

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

默認(rèn)的類的私有屬性不能訪問:
面向?qū)ο缶幊袒A(chǔ)
通過將其私有屬性包裝成方法進(jìn)行使用,其是可以訪問的。
面向?qū)ο缶幊袒A(chǔ)

實(shí)例如下:

#!/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())  # 此處的返回為空,因?yàn)闆]有滿足的條件
print (jerry.age)  # 可通過此種方式進(jìn)行返回,因此其是無法真實(shí)控制的

結(jié)果如下
面向?qū)ο缶幊袒A(chǔ)

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

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

    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的方法進(jìn)行調(diào)用嗎
print  (jerry.charget()) #此處可有方法進(jìn)行調(diào)用處理
print  (jerry.__dict__) # 事實(shí)上,此處的私有屬性在python中是修改了名稱,其結(jié)果是實(shí)例名._類名稱__age可進(jìn)行調(diào)用,具體可參見__dict__獲取,此處私有屬性是在實(shí)例字典中存在的
print (jerry._Person__age)
jerry._Person__age=300  #修改私有屬性參數(shù)值,但其測(cè)試,顯示中既然是私有屬性,則不建議修改其屬性值
print (jerry._Person__age)  # 修改結(jié)果查看

結(jié)果如下
面向?qū)ο缶幊袒A(chǔ)

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

2 保護(hù)變量

在變量名前使用一個(gè)下劃線,稱為保護(hù)變量

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

結(jié)果如下
面向?qū)ο缶幊袒A(chǔ)

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

3 私有方法

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

結(jié)果如下
面向?qū)ο缶幊袒A(chǔ)

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

4 總結(jié)

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

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

7 屬性裝飾器

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

一般方法

#!/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())

結(jié)果如下
面向?qū)ο缶幊袒A(chǔ)

屬性裝飾器

#!/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和上面的和下面的必須相同,否則會(huì)出現(xiàn)錯(cuò)誤
    def  getname(self,value):
        self.__name=value
    @getname.deleter  # 刪除屬性值,使用del  self.__name 進(jìn)行對(duì)屬性的刪除操作
    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  #外部調(diào)用執(zhí)行刪除程序

結(jié)果如下

面向?qū)ο缶幊袒A(chǔ)

第二種寫法

#!/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): # 只有g(shù)etter屬性,此中成為只讀屬性
        self.__english=value
#第二種寫方式,初始化方法
    eng=property(lambda   self:self.__english,seteng ) # property 是只讀的,此處的傳入的值,eng是一個(gè)函數(shù),第一個(gè)是gettter,第二個(gè)是setter

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

結(jié)果如下

面向?qū)ο缶幊袒A(chǔ)

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

面向?qū)ο缶幊袒A(chǔ)

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

@屬性名.seeter: 給屬性賦值時(shí)先做判斷; 當(dāng)屬性名=value會(huì)自動(dòng)調(diào)用該函數(shù)

通過deleter 方法可以使得當(dāng)del 屬性名, 會(huì)自動(dòng)調(diào)用該函數(shù)并執(zhí)行高函數(shù)下面的操作~~

面向?qū)ο缶幊袒A(chǔ)

特別注意: 這三個(gè)方法同名

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

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

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

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

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


@property應(yīng)用:
用于分頁(yè)顯示,此處傳入兩個(gè)值,一個(gè)值是第幾頁(yè),另一個(gè)是每頁(yè)的數(shù)量,通過@property 可以進(jìn)行對(duì)列表的操作。

面向?qū)ο缶幊袒A(chǔ)

8 對(duì)象的銷毀

類中可以定義del方法,稱為析構(gòu)函數(shù)(方法)
作用: 銷毀類的實(shí)例的時(shí)候調(diào)用,用以釋放占用的資源 ,其目標(biāo)是銷毀實(shí)例

由于python實(shí)現(xiàn)了垃圾回收機(jī)制,這個(gè)方法不能確定何時(shí)使用,有必要的時(shí)候,請(qǐng)使用del語(yǔ)句刪除實(shí)例

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

a=Person('tom')
del a # 調(diào)用消亡實(shí)例

結(jié)果如下
面向?qū)ο缶幊袒A(chǔ)

9 方法重載 (overload)

在其他面向?qū)ο蟮母呒?jí)語(yǔ)言中,都有重載的概念,所謂的重載,就是同一個(gè)方法名,但是參數(shù)數(shù)量,類型不一樣,就是同一個(gè)方法的重載

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

10 練習(xí)

1 隨機(jī)數(shù)生成類,可以指定一批生成的個(gè)數(shù),可以指定數(shù)值的范圍,生成隨機(jī)數(shù)

#!/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))

結(jié)果如下:

面向?qū)ο缶幊袒A(chǔ)

2 隨機(jī)生成隨機(jī)數(shù),并兩兩配對(duì)形成二維坐標(biāo)系,把這些坐標(biāo)組織起來,并進(jìn)行打印

#!/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()  # 此處可使用此種方式將內(nèi)部的方法以屬性的形式進(jìn)行處理,供內(nèi)部調(diào)用
    def  _generate(self):
        while True:
            yield  [random.randint(self.min,self.max)  for _ in range(self.count)]  #c此處返回一個(gè)列表生成式,用于下面的next()進(jìn)行調(diào)用
    def genrate(self,count):
        self.count=count
        return   next(self.g)  # 此處通過此種方式進(jìn)行處理,返回值為隨機(jī)數(shù)的單個(gè)個(gè)體

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

結(jié)果如下
面向?qū)ο缶幊袒A(chǔ)

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

#!/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:  # 信息本身也是一個(gè)對(duì)象,一個(gè)容器的對(duì)象
    def  __init__(self):
        self.lst = []
    def addcar(self,car:Car):  #此處可調(diào)用上述的類
        self.lst.append(car)
    def getall(self):
        return  self.lst  # 打印的格式化輸出

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

四 python 補(bǔ)丁

1 概述

可以通過修改或者替換成員,使用者調(diào)用方?jīng)]改變,但是,類提供的功能可能已經(jīng)修改了,當(dāng)項(xiàng)目需要修改較多的類時(shí),又著急上線,修改起來比較麻煩時(shí),需要通過在一個(gè)文件中指定某個(gè)類進(jìn)行相關(guān)的操作來對(duì)其進(jìn)行打補(bǔ)丁
猴子補(bǔ)丁
運(yùn)行時(shí),對(duì)屬性進(jìn)行動(dòng)態(tài)替換
黑魔法,慎用

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())

默認(rèn)結(jié)果如下
面向?qū)ο缶幊袒A(chǔ)
補(bǔ)丁文件

test1

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

def  getscore(self):  # 其名稱和相關(guān)的類對(duì)象名必須一致,否則會(huì)報(bào)錯(cuò),此處返回是一個(gè)字典
    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  # 對(duì)類來改變屬性,在類的DICT中有方法,
student = Person(80,91,80)

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

結(jié)果如下
面向?qū)ο缶幊袒A(chǔ)

五 面向?qū)ο蟮娜筇匦灾庋b

1 概念

組裝: 將數(shù)據(jù)和操作組裝到一起
隱藏?cái)?shù)據(jù):對(duì)外只暴露一些接口,通過接口訪問對(duì)象
封裝實(shí)際上是把數(shù)據(jù)封裝到摸個(gè)地方,以后再去調(diào)用在某處的內(nèi)容或者數(shù)據(jù)
調(diào)用封裝數(shù)據(jù)的方式
通過對(duì)象直接調(diào)用
其中init表示的是一個(gè)構(gòu)造器,當(dāng)類的實(shí)例化過程時(shí)會(huì)調(diào)用其方法,由于其是必選參數(shù),因此在傳值時(shí)必須與對(duì)應(yīng)的個(gè)數(shù)相同,當(dāng)然可以實(shí)例化多個(gè)對(duì)象

2 相關(guān)實(shí)例

面向?qū)ο缶幊袒A(chǔ)
面向?qū)ο缶幊袒A(chǔ)
通過self在類內(nèi)部對(duì)類中的數(shù)據(jù)進(jìn)行調(diào)用
面向?qū)ο缶幊袒A(chǔ)
類的實(shí)例化
面向?qū)ο缶幊袒A(chǔ)
面向?qū)ο缶幊袒A(chǔ)

六面向?qū)ο蟮娜筇匦灾^承(inheritance)

1 概念

多復(fù)用,繼承來的就不用自己寫了
多繼承少修改,OCP,使用繼承來改變,來提現(xiàn)個(gè)性
基類和派生類
其中父類也叫基類
子類也叫派生類

父類

面向?qū)ο缶幊袒A(chǔ)

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

面向?qū)ο缶幊袒A(chǔ)

子類的實(shí)例化和結(jié)果,其完全繼承和父類的屬性和方法:

面向?qū)ο缶幊袒A(chǔ)

當(dāng)子類中有自己的構(gòu)造函數(shù)時(shí),以子類中的構(gòu)造函數(shù)為準(zhǔn)

面向?qū)ο缶幊袒A(chǔ)
面向?qū)ο缶幊袒A(chǔ)

2 基本實(shí)踐

1 共有屬性和方法繼承

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

class C(B):
    pass

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

結(jié)果如下

面向?qū)ο缶幊袒A(chǔ)

2 私有屬性的繼承

默認(rèn)的,私有屬性不能直接調(diào)用,需要偽裝成方法方可調(diào)用。

面向?qū)ο缶幊袒A(chǔ)

#!/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__) # 默認(rèn)的A的私有屬性會(huì)被B讀取,并可進(jìn)行訪問,其方法亦可被訪問
print (tom.getage)

結(jié)果如下
面向?qū)ο缶幊袒A(chǔ)

3 私有方法繼承

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

面向?qū)ο缶幊袒A(chǔ)

#!/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__)  # 私有方法是屬于類的,不能被繼承,

結(jié)果如下:
面向?qū)ο缶幊袒A(chǔ)

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

1 基本含義

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

2 基本實(shí)例

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

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

結(jié)果如下
面向?qū)ο缶幊袒A(chǔ)

類和實(shí)例屬性列表打印

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

class C(B):
    pass
tom=C('tom')
print (tom.__dict__)  # 打印實(shí)例的字典,其中只有實(shí)例化的屬性的列表
print (C.__dict__)  # 打印C類的字典,C類無任何屬性和方法,因?yàn)槠涫莵碜杂贐和A的
print (B.__dict__) # 打印B類的字典,其中有x類屬性的定義,shout函數(shù)的定義
print (A.__dict__)# A中包括初始化函數(shù)等信息

如下
面向?qū)ο缶幊袒A(chǔ)

3 結(jié)論

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

4 重寫父類的構(gòu)造函數(shù)

1 python 2.0 中的寫法

A.init(self,args)

1 通過 父類名.init(self,父類參數(shù)) 進(jìn)行重寫構(gòu)造函數(shù)

面向?qū)ο缶幊袒A(chǔ)

2 通過super對(duì)私有方法重寫

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

優(yōu)點(diǎn): 不需要明確告訴父類的名稱,如果父類改變,只需修改class 語(yǔ)句后面的繼承關(guān)系即可,不用修改父類名稱

面向?qū)ο缶幊袒A(chǔ)
實(shí)例化調(diào)用
面向?qū)ο缶幊袒A(chǔ)

#!/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內(nèi)容
        print (super(B,self))# 此處相當(dāng)于將B設(shè)置為self傳輸進(jìn)去。等價(jià)與上面的super
        super().shout()#此處調(diào)用的是父類并調(diào)用了父類的shout
a=B()
a.shout()

結(jié)果如下
面向?qū)ο缶幊袒A(chǔ)

#!/usr/bin/poython3.6
#conding:utf-8
class  A:
    x=123
    def __init__(self,name='tom',age=0):
        self.__name=name # 此處是實(shí)例的初始化函數(shù),當(dāng)然要在其實(shí)例化后才能表現(xiàn)出該屬性,該類中也找不到,當(dāng)然在其子類中也不能找到
        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 # 此處的傳入后會(huì)修改成50,因?yàn)槠涫窃谛薷闹髠魅階類中進(jìn)行處理的
        super().__init__(name,age) #新式類推薦使用的方式,此處默認(rèn)傳入self,需要送入指定參數(shù),此處不許找指定self和父類的類名
        self.__name=name+" "+'B' # 私有屬性的修改是無法生效的,因?yàn)槠渌接袑傩缘膋ey和類名相關(guān),
        # self.age=10  #覆蓋共有屬性,其之前的age是0,現(xiàn)在覆蓋成10,因?yàn)閷?shí)例的字典中的key是唯一的。此處因?yàn)樵赼.__init__(self,name)之后定義,因此其會(huì)
        # 覆蓋之前name傳入的值
    def shout(self):
        print ('B shout')
        super(B,self).shout()  #此處是調(diào)用父類的,通過super可以方面的訪問自己的祖先類,其查找順序是找本實(shí)例,若通過本實(shí)例查找
        #其類,通過類查找其父類并進(jìn)行相關(guān)的操作
        super().shout() # 與上述相同
x=B('jerry',30) # 此處傳入的值是30,由于下面的覆蓋,變成了10
x.shout()
print (x.__dict__)  #其私有屬性的_A__name 仍然存在,_B__name 也存在,
print (x.getname)

結(jié)果如下

面向?qū)ο缶幊袒A(chǔ)

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

1 公有屬性覆蓋和父類公有屬性調(diào)用

#!/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):  #此處進(jìn)行初始化,初始化后,其父類的公有屬性將不能被調(diào)用
        self.name=name #此處重新覆蓋了共有屬性name,由之前的tom修改成了jerry
    def shout(self):
        print ('B shout')
tom=B('jerry')
print (tom.name)
print (tom.__dict__)
#print  (tom.age) # 此處是父類的屬性,此處不能被調(diào)用

結(jié)果如下

面向?qū)ο缶幊袒A(chǔ)

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):  #此處進(jìn)行初始化,初始化后,其父類的公有屬性將不能被調(diào)用
        self.cname=name #此處定義了公有屬性cname
    def shout(self):
        print ('B shout')
tom=B('jerry')
print (tom.cname)
# print (tom.name)  #此處是父類的屬性,不能被調(diào)用,因?yàn)樽宇愡M(jìn)行了初始化
print (tom.__dict__) # 此處只有cname
#print  (tom.age) # 此處是父類的屬性,此處不能被調(diào)用

面向?qū)ο缶幊袒A(chǔ)

3 調(diào)用父類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):  #此處進(jìn)行初始化,此處需要調(diào)用父類的方法,若不調(diào)用,則默認(rèn)父類的私有方法不能使用,但共有方法可以被調(diào)用
        self.cname=name #此處重新定義了共有屬性cname,
        A.__init__(self,name) # 此處調(diào)用了父類的name
    def shout(self):
        print ('B shout')
tom=B('jerry')
print (tom.name) #此處name存在于A類中。而B中調(diào)用A.__init__(self,args) 導(dǎo)致其A中存在的屬性在B中也同樣存在
print (tom.cname)
print (tom.__dict__)# 其共有方法中的name存在,也有自己的初始化的cname

結(jié)果如下

面向?qū)ο缶幊袒A(chǔ)

4 共有屬性中順序不同導(dǎo)致的問題

#!/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 # 此處的傳入后會(huì)修改成30,因?yàn)槠涫窃谛薷闹髠魅階類中進(jìn)行處理的
        A.__init__(self,name,age)  #通過父類的調(diào)用來調(diào)用父類的屬性,此中可調(diào)用父類的私有屬性,
        self.__name=name+" "+'B' # 私有屬性的修改是無法生效的,因?yàn)槠渌接袑傩缘膋ey和類名相關(guān),
        self.age=10  #覆蓋共有屬性,其之前的age是0,現(xiàn)在覆蓋成10,因?yàn)閷?shí)例的字典中的key是唯一的。此處因?yàn)樵赼.__init__(self,name)之后定義,因此其會(huì)
        # 覆蓋之前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)

結(jié)果如下
面向?qū)ο缶幊袒A(chǔ)

#!/usr/bin/poython3.6
#conding:utf-8
class  A:
    x=123
    def __init__(self,name='tom',age=0):
        self.__name=name # 此處是實(shí)例的初始化函數(shù),當(dāng)然要在其實(shí)例化后才能表現(xiàn)出該屬性,該類中也找不到,當(dāng)然在其子類中也不能找到
        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 # 此處的傳入后會(huì)修改成50,因?yàn)槠涫窃谛薷闹髠魅階類中進(jìn)行處理的
        A.__init__(self,name,age)  #通過父類的調(diào)用來調(diào)用父類的屬性,此中可調(diào)用父類的私有屬性,
        self.__name=name+" "+'B' # 私有屬性的修改是無法生效的,因?yàn)槠渌接袑傩缘膋ey和類名相關(guān),
        # self.age=10  #覆蓋共有屬性,其之前的age是0,現(xiàn)在覆蓋成10,因?yàn)閷?shí)例的字典中的key是唯一的。此處因?yàn)樵赼.__init__(self,name)之后定義,因此其會(huì)
        # 覆蓋之前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)

結(jié)果如下
面向?qū)ο缶幊袒A(chǔ)

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

1 私有屬性未處理和調(diào)用

#!/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 # 此處進(jìn)行覆蓋共有屬性,其私有屬性未處理
    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 # 此處進(jìn)行覆蓋私有屬性,其私有屬性未處理
    def shout(self):
        print ('B shout')

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

結(jié)果如下

面向?qū)ο缶幊袒A(chǔ)

3 調(diào)用父類方法獲取共有屬性和私有屬性列表

#!/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)  #調(diào)用父類方法,處理獲取屬性
    def shout(self):
        print ('B shout')

b=B('jerry',30)
print (b.__dict__)  # 此處可獲取父類中的共有屬性和私有屬性
print  (b.name,b._A__age)  # 已經(jīng)進(jìn)行了修改,,因?yàn)樯厦娴膁ef  __init__(self,name,age): 中的age在實(shí)例化后已經(jīng)被修改
print (b.getage)

結(jié)果如下
面向?qū)ο缶幊袒A(chǔ)

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)  #調(diào)用父類方法,處理獲取屬性
        self.__age=age # 重覆蓋age,但無法完成,因?yàn)槠渌接袑傩院皖惷嘘P(guān),其相當(dāng)于增加
    def shout(self):
        print ('B shout')

b=B('jerry',30)
print (b.__dict__)  # 此處可獲取父類中的共有屬性和私有屬性
print  (b.name,b._A__age)  # 已經(jīng)進(jìn)行了修改,,因?yàn)樯厦娴膁ef  __init__(self,name,age): 中的age在實(shí)例化后已經(jīng)被修改
print (b.getage)

結(jié)果如下
面向?qū)ο缶幊袒A(chǔ)

6 類方法和靜態(tài)方法繼承中的初始化

1 實(shí)例

#!/usr/bin/poython3.6
#conding:utf-8
class  A:
    @classmethod
    def class_method(cls):
        print (cls.__name__)  # 此處打印類的名稱
    @staticmethod  # 此處定義其靜態(tài)方法
    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()  # 此處因?yàn)檎{(diào)用的是C類,因此其cls.__name__打印的是C而并非是B
b.static_method()  # 此處的靜態(tài)方法是B,因?yàn)镃中未定義靜態(tài)方法
print (B.__dict__)

a=A()
a.class_method()  # 此處因?yàn)檎{(diào)用的時(shí)A類,因此cls.__name__打印的是A
a.static_method()
print (A.__dict__)

結(jié)果如下
面向?qū)ο缶幊袒A(chǔ)

2 結(jié)論

靜態(tài)方法會(huì)進(jìn)行屬性查找,找到及截至,類方法因?yàn)轭惖睦^承而進(jìn)入該C類中,而C類調(diào)用其類方法后得到的名稱當(dāng)然是C自己,此處若只有A配置類屬性,則B和C進(jìn)行調(diào)用,則各自cls.name 仍然是各個(gè)實(shí)例的類,相當(dāng)于自己在調(diào)用自己的類。此處大大的復(fù)用了,此處就是多態(tài)

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): # 此處進(jìn)行初始化,方法不能繼承
        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__)  # 私有方法是屬于類的,不能被繼承,

結(jié)果如下

面向?qū)ο缶幊袒A(chǔ)

8 多繼承

1 多重繼承簡(jiǎn)介

原則: OCP 原則,多繼承,少修改
繼承的用途: 增強(qiáng)基類,實(shí)現(xiàn)多態(tài)

多態(tài):在面向?qū)ο蟓h(huán)境中,父類,子類通過繼承聯(lián)系到一起,如果可以通過一套方法,就可以實(shí)現(xiàn)不同表現(xiàn),這就是多態(tài)

一個(gè)類繼承多個(gè)類就是多繼承,它具有多個(gè)類的特征

多態(tài)是繼承+多態(tài)


多態(tài)的弊端:
1 多繼承很好的模擬了世界,因?yàn)槭挛锖苌偈菃我焕^承,但是舍棄簡(jiǎn)單,必然引入復(fù)雜性,帶來了沖突
多繼承的實(shí)現(xiàn)會(huì)導(dǎo)致編譯器設(shè)計(jì)的復(fù)雜度增加,所以很多語(yǔ)言都舍棄了類的多繼承。

多繼承可能帶來二義性
解決方案
實(shí)現(xiàn)多繼承的語(yǔ)言,需要解決二義性和,深度優(yōu)先或廣度優(yōu)先

面向?qū)ο缶幊袒A(chǔ)

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

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


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


多繼承的缺點(diǎn)

當(dāng)類很多時(shí),繼承復(fù)雜的情況下,繼承路徑太多,很難說清什么樣的繼承路徑

Python 語(yǔ)法是允許多繼承,但python代碼是解釋器執(zhí)行的,只有執(zhí)行到的時(shí)候,才發(fā)現(xiàn)錯(cuò)誤

團(tuán)隊(duì)協(xié)作開發(fā),如果引入多繼承,代碼將不可控

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

面向?qū)ο缶幊袒A(chǔ)

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

面向?qū)ο缶幊袒A(chǔ)

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

面向?qū)ο缶幊袒A(chǔ)

對(duì)于新式類,是廣度優(yōu)先
對(duì)于經(jīng)典類,是深度優(yōu)先

面向?qū)ο缶幊袒A(chǔ)

對(duì)于新式類,當(dāng)調(diào)用D實(shí)例時(shí),其結(jié)果是執(zhí)行D的輸出,當(dāng)D中為pass 占位,其繼承了B的輸出,當(dāng)B中的結(jié)果為pass 時(shí),其會(huì)繼承C中的輸出,

對(duì)于經(jīng)典類,當(dāng)調(diào)用D實(shí)例時(shí),其結(jié)果是執(zhí)行D的輸出,當(dāng)D中為pass 占位,其繼承了B的輸出,當(dāng)B中的結(jié)果為pass 時(shí),其會(huì)繼承A中的輸出,因?yàn)锽繼承了A,因?yàn)槠涫巧疃葍?yōu)先。

9 總結(jié)

繼承時(shí),共有的,子類和實(shí)例都可以隨意訪問,私有成員被隱藏,子類和實(shí)例不可直接訪問,當(dāng)私有變量所在的類內(nèi)的方法中可以訪問這個(gè)私有變量,python通過自己一套實(shí)現(xiàn),實(shí)現(xiàn)和其他語(yǔ)言一樣的面向?qū)ο蟮睦^承機(jī)制


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

七 Mixin

1 簡(jiǎn)介

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

Mixin
面向?qū)ο缶幊袒A(chǔ)

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


Mxin 本質(zhì)上是多繼承實(shí)現(xiàn)的
mixin 提現(xiàn)的是一種組合的設(shè)計(jì)模式
在面向?qū)ο蟮脑O(shè)計(jì)中,一個(gè)復(fù)雜的類,往往需要很多功能,而這些功能有來自不同的類提供,這些就需要很多組合在一起。


MIxin 類的使用原則:
Mixin 類中不應(yīng)該顯式的出現(xiàn)__init__初始化方法
Mixin 類通常不能獨(dú)立工作,因?yàn)樗菧?zhǔn)本混入別的類中的部分功能實(shí)現(xiàn)
Mixin 類的祖先類也應(yīng)該是Mixin類


Mixin類和裝飾器

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

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

1 子類中欲實(shí)現(xiàn)自己的功能自己處理,不再父類中進(jìn)行定義

#!/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()

結(jié)果如下
面向?qū)ο缶幊袒A(chǔ)

2 通過父類中實(shí)現(xiàn)。子類若需要個(gè)性化需求,則自己修改

#!/usr/local/bin/python3.6
#coding:utf-8
def  printable(cls):  #此處傳入的是類函數(shù)
    def _print(self):  # 此處的實(shí)例可以看作是這個(gè)類函數(shù)的參數(shù)
        print (self.content,'裝飾器')  # 此處是計(jì)算的返回值
    cls.print=_print # 此處對(duì)返回值進(jìn)行賦值
    return  cls  # 并返回該函數(shù)的執(zhí)行結(jié)果
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()

結(jié)果如下

面向?qū)ο缶幊袒A(chǔ)

另一種方式,使用lambda 進(jìn)行處理

#!/usr/local/bin/python3.6
#coding:utf-8
def  printable(cls):
    # 此處是給對(duì)應(yīng)的類增加相關(guān)的屬性,使得其更加健壯
    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)

結(jié)果如下
面向?qū)ο缶幊袒A(chǔ)

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兩個(gè)類,此處是多繼承,Mixin繼承了object。而Pdf繼承了Docment
    # 而Docment 繼承了object,此處的作用是使用PrintableMixin覆蓋之前的DOcment才是要求,一般的Mixin都是在最前面放的
    pass
print (PrintablePdf.mro()) #查看MRO的輸出,以MRO的輸出為準(zhǔn)
Pdf=PrintablePdf('test\nabc')
Pdf.print()

結(jié)果如下
面向?qū)ο缶幊袒A(chǔ)

調(diào)換位置如下

#!/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兩個(gè)類,此處是多繼承,Mixin繼承了object。而Pdf繼承了Docment
    # 而Docment 繼承了object,此處的作用是使用PrintableMixin覆蓋之前的DOcment才是要求,一般的Mixin都是在最前面放的
    pass
print (PrintablePdf.mro()) #查看MRO的輸出,以MRO的輸出為準(zhǔn)
Pdf=PrintablePdf('test\nabc')
Pdf.print()

結(jié)果如下:
面向?qū)ο缶幊袒A(chǔ)

#!/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): # 此處使用類的繼承來打印一個(gè)超類
    def print(self):
        print ('~'*20)
        super().print()
        print('~'*20)
class PrintablePdf(SuperPrintableMixin,Pdf): 
    pass
print (PrintablePdf.mro()) #查看MRO的輸出,以MRO的輸出為準(zhǔn)
Pdf=PrintablePdf('test\nabc')
Pdf.print()

結(jié)果如下

面向?qū)ο缶幊袒A(chǔ)

4 結(jié)論如下:

裝飾器使用的是函數(shù)的方式實(shí)現(xiàn)的,其函數(shù)只能實(shí)現(xiàn)封裝功能,而Mixin是一個(gè)類,其類可實(shí)現(xiàn)封裝,繼承,和多態(tài)的特性。

向AI問一下細(xì)節(jié)
AI