1. 程式人生 > >python(類詳解)

python(類詳解)

面向物件,面向過程的區別:
面向過程開發,以函式作為基本結構使用:
面向物件的開發,以物件作為基本結構使用
語言中物件結構的特色:高內聚,低耦合。
類的定義
類是一個實物的特徵的集合,是抽象的概念。
類和物件的關係 類是多個物件歸納總結而來的,是一種概念,包含所有物件。

由物件總結出類的過程,叫做抽象化 物件是類的具體實現或者實施而來,他是真實的,特指某個事物
由類製作出物件的過程,叫做例項化 如何書寫類檔案 推薦使用駝峰命名法來書寫檔案
定義規則:
類名:每個單詞的首字母大寫
函式名 /變數:除了第一個單詞之外首字母大寫
類的組成:
一個檔案如果是一個單獨的類,那麼直接使用類名來當作檔名即可
類的組成 類中只有2種內容:成員屬性和成員方法
成員屬性: 用於描述類的特徵的變數就是成員屬性
成員方法: 用於描述類的功能的函式就是成員方法
類的書寫規則 1.必須使用class關鍵字來宣告一個類
2.類的名稱需要符合駝峰命名法(規範)
3.類中只能存在2種內容,成員屬性和成員方法,除此之外,所有的程式碼都禁止出現!
4.宣告成員屬性時,所有成員屬性必須有值,如果沒值會報錯!,推薦使用None等值代替
5.成員方法只需要按照函式的規則宣告即可例項化物件 例項化物件格式
注意:在定義類的成員方法的時候,方法的引數至少有一個self。相當於c++中的this指標
class A():
def fun():
print(“我和石頭做遊戲”)
編譯報錯:
File “E:/project/作業_20180821.py”, line 199
path = “D:\”"
^
SyntaxError: EOL while scanning string literal

init(self):python中的構造方法
除類self之外,還可以傳入其他引數。通過重寫方法制定這些初始化操作

私有,公有
私有: 在變數和函式名前加上兩個下劃線(__)就可以類
注意:私有變數在類外不能進行訪問和修改,只能通過內部訪問和修改(呼叫get,set函式)

繼承 lass 子類(父類) :
#繼承操作的關鍵步驟 pass 繼承的特徵
1.所有類都是繼承自object類(object類對應的物件就是object物件,也是萬物皆物件)
2.子類繼承父類則可以訪問父類的所有成員。(私有成員除外)
3.子類繼承父類並不會將父類的所有成員複製到子類當中去,訪問父類成員是間接通過父類來訪問的,
4.子類可以具有自己獨有的屬性和方法
5.子類可以過載父類中的方法,只需要設定和父類指定成員相同的名稱即可實現過載,過載之後的成員,子類只會訪問當前類中的成員,而不會呼叫父類中同名的成員
6.子類中如果過載父類的方法,並且還想將過載的父類方法借調過來使用,可以在過載的方法中使用如下方法 父類名.方法()或者 super().方法() 單繼承和多繼承 單繼承:每個類只能繼承一個類的方式稱為單繼承。
7.父類中的私有化方法和屬性不能

被繼承
父類名.方法(self) :呼叫未繫結的父類方法
格式:父類名.方法(self)
注意:當使用這種方法呼叫父類的方法時候,父類的方法的引數是子類的例項化物件
super(). :使用super()函式
格式:super().父類名.方法()
注意:這裡不需要將自物件的例項化引數self傳入
注意:對於你定義的每一個類,python會計算出一個方法解析順序(MRO)列表,這個MRO列表就是一個簡單的所有基類的線性順序列表,python會在MRO列表上從左到右開始查詢基類,直到找到第一個匹配這個屬性的類為止。
補充: 類名.mro()可以檢視該類對應的MRO表
多重繼承:
Class 類名(要繼承的類1,要繼承的類2):
子類的程式碼塊
注意:當子類繼承類多個類的時候,當多個父類的成員方法名不相同時候,則子類都可以進行訪問呼叫,如果多個父類的成員方法的方法名是相同的時候,則當子類呼叫時候,只按照繼承順序的先後進行呼叫,呼叫一次後,不會在呼叫。
Eg:
class Base1:
def fool(self):
print(“我是Bool1”)
class Base2:
def fool(self):
print(“我是Bool2”)
class C(Base1, Base2):
pass
c = C()
c.fool()
實行結果:我是Bool1
類的組合:把類的例項化放到新類裡面,一般是把幾個不是繼承關係的類放到一起(沒有縱向關係(繼承關係),有橫向關係)
補充:一個類的成員變數可以是另一個類的例項化物件(兩個類之間沒有繼承關係)
Eg:
class Turtle:
def init
(self, x):
self.num = x
class Fish:
def init(self, x):
self.num = x
class Pool:
def init(self, x, y):
self.turtle = Turtle(x)
self.fish = Fish(y)
def display(self):
print(self.turtle.num, self.fish.num, sep="\t")
pool = Pool(2,9)
pool.display()
實行結果:2 9
註解:Pool類中的成員變數分別是Turtle和Fish的例項化物件
程式呼叫順序:
1.Pool例項化物件,呼叫Pool的建構函式,將兩個引數傳入,在建構函式中,對Turtle和Fish分別例項化物件,將傳入Pool建構函式的兩個引數傳入到Turtle和Fish例項化的建構函式中,例項化turtle和fish作為Pool的例項化物件pool的成員變數.pool呼叫display()函式時,又分別訪問了turtle和fish的成員變數num。
繼承有兩種用途:
一:繼承基類的方法,並且做出自己的改變或者擴充套件(程式碼重用)
二:宣告某個子類兼容於某基類,定義一個介面類Interface,介面類中定義了一些介面名(就是函式名)且並未實現介面的功能,子類繼承介面類,並且實現介面中的功能

補充:
當類之間有顯著不同,並且較小的類是較大的類所需要的元件時,用組合比較好
類,類物件,例項物件
1.物件屬性,例項化物件屬性
以下程式碼中,類:C 類物件:CC 例項化物件:a,b,d
class C:
count = 0
a = C()
b = C()
d = C()
print(a.count, b.count, d.count) # 1
d.count += 10
print(a.count, b.count, d.count) # 2 修改的是d的例項化物件屬性,發生變化的是d的count屬性,a和b是分別例項化的它們的值不會修改
C.count = 100
print(a.count, b.count, d.count) # 3 修改的是類C的靜態屬性值,a和c沒對其例項化屬性修改過,而d之前對例項化物件的屬性賦值,覆蓋了類的靜態屬性
實行結果:
0 0 0
0 0 10
100 100 10

2.例項化物件的屬性和方法相同,則屬性覆蓋方法
class A:
def display(self):
print(“AAAAA”)
a = A()
a.display()
a.display = 1 #python中物件不需要宣告,直接賦值就是定義。給例項化物件a的display屬性直接賦值,該屬性和方法名相同,則屬性名直接將方法名覆蓋掉
a.display()
實行結果:
AAAAA
Traceback (most recent call last):
File “E:/project/作業_20180821.py”, line 372, in
a.display()
TypeError: ‘int’ object is not callable

Python中約定俗成的規定:
1.不要試圖在一個類裡定義所有能想到的特性和方法,應該利用繼承和組合機制進行拓展
2.用不同的詞性命名,如屬性名用名詞,方法名用動詞
繫結
定義:python嚴格要求方法需要有例項才能被呼叫,這種限制就是繫結
1.
class A:
def display():
print(“AAAAA”)
A.display() # 類物件A直接呼叫displa方法不會報錯
a = A()
a.display() # 例項化物件a直接呼叫display方法會報錯,因為呼叫display時把a當作引數傳進去了,完整的呼叫方法a.display(a)
實行結果:
AAAAA
Traceback (most recent call last):
File “E:/project/作業_20180821.py”, line 379, in
a.display()
TypeError: display() takes 0 positional arguments but 1 was given
2.
class CC:
def setXY(self, x, y):
self.x = x
self.y = y
def printXY(self):
print(self.x, self.y)
dd = CC()
print(“dd:dict”, dd.dict) # 檢視物件所有的屬性,返回一個字典的型別。字典中只有例項物件的屬性,不顯示類屬性和特殊屬性(魔術方法)
print(“CC:dict”, CC.dict) # 鍵表示屬性名,值就是屬性的值
dd.setXY(4, 6)
print(“dd:dict”, dd.dict) # 這x,y只屬於dd這個例項化物件
print(“CC:dict”, CC.dict) # 類屬性中,沒有4和6,這歸功於繫結,例項化物件dd呼叫setxy(4,5)實際上將自身當作引數傳入進去,完整的函式是dd.setxy(dd,4,6),在函式中賦值是dd.x = 4,dd.y = 6,所以4,6只屬於dd
del CC

ee = CC() # 出錯,類物件已經刪除

dd.printXY()

實行結果:
dd:dict {}
CC:dict {‘module’: ‘main’, ‘setXY’: <function CC.setXY at 0x00000000029D7598>, ‘printXY’: <function CC.printXY at 0x00000000029D7620>, ‘dict’: <attribute ‘dict’ of ‘CC’ objects>, ‘weakref’: <attribute ‘weakref’ of ‘CC’ objects>, ‘doc’: None}
dd:dict {‘x’: 4, ‘y’: 6}
CC:dict {‘module’: ‘main’, ‘setXY’: <function CC.setXY at 0x00000000029D7598>, ‘printXY’: <function CC.printXY at 0x00000000029D7620>, ‘dict’: <attribute ‘dict’ of ‘CC’ objects>, ‘weakref’: <attribute ‘weakref’ of ‘CC’ objects>, ‘doc’: None}
4 6

注意:類中定義的屬性和方法是靜態變數,就算類物件被刪除了,它們還是存放在記憶體中,只有在程式退出時候,它們才會被釋放。(建議程式設計時使用例項屬性,不要使用類屬性)
與類,物件相關的BIF(內建函式)
1.issubclass(class1,class2)
如果class1是class2的子類就返回True
注意:1.檢查是非嚴格行的檢查(一個類也是本身的子類)
2.class2類物件組成的元組,只要class1是其中任何一個候選類的子類則返回True
3.其他情況會丟擲一個TypeError異常
2.isinstance(class1,class2)
注意:1.如果第一個引數傳的不是物件型別,則永遠返回False
2.第二個引數不是類或者不是由類組成的元組,則丟擲一個TypeError的異常
3.第一個引數是一個類的例項化物件,第二個是這個類物件時,返回True
3.hasattr(object,name)
查詢物件是否有指定的屬性
第一個是物件,第二個是屬性名,用雙引號串起來,否則報錯
4.getattr(object,name[,default])
返回物件指定的屬性值,如果該物件沒有其屬性,則返回制定的預設值,如果沒有屬性還有沒有設定預設返回值,則丟擲一個AttrbuteError異常
5.setattr(object,name,value)
如果物件沒有屬性值,則設定物件指定的屬性值,
6.delattr(object,name)
刪除物件是否有指定的屬性,如果物件沒有其屬性值,則丟擲一個AttrbuteError異常
注意:object可為例項化物件,也可為類物件
7.property(fget = None , fset=None , fdel = None, doc=None)
含義:通過屬性來設定屬性,第一個引數獲得屬性的方法,第二個引數設定屬性的方法,第三個引數刪除屬性的方法。
優點:不需要修改介面a,只需要修改函式名和property的引數名,通過一個或多個魔術發方法實現
class DD:
def init(self, x = 10):
self.x = x
def getx(self):
return self.x
def setx(self, x):
self.x = x
def delx(self):
del self.x
a = property(getx, setx, delx)

dd = DD()
print(dd.getx())
print(dd.x)
print(dd.a)
dd.a = 19
print(dd.getx())
print(dd.x)
print(dd.a)
del dd.a
print(dd.getx()) #執行時會報錯
print(dd.x)
print(dd.a)
實行結果:
10
10
10
19
19
19