部落格核心内容:
面向過程與面向對象的對比
id、type和value的講解
類和對象的概念
初始化構造函數__init__的作用
self關鍵字的使用
繼承的概念
組合的概念
接口的概念
抽象類的概念
屬性與方法周遊順序的問題(MRO清單)
super關鍵字的使用
多态的概念
封裝的概念
@property的用法
綁定方法與非綁定方法
staticmethod與classmethod的差別
綜合應用的一個小例子
1、面向過程與面向對象的對比
面向過程的程式設計的核心是過程(流水線式思維),過程即解決問題的步驟,面向過程的設計就好比精心設計好一條流水線,考慮周全什麼時候處理什麼東西。
優點是:極大的降低了程式的複雜度
缺點是:一套流水線或者流程就是用來解決一個問題,生産汽水的流水線無法生産汽車,即便是能,也得是大改,改一個元件,牽一發而動全身。
應用場景:一旦完成基本很少改變的場景,著名的例子有Linux內核,Git,以及Apache HTTP Server等。
面向對象的程式設計的核心是對象(上帝式思維),要了解對象為何物,必須把自己當成上帝,上帝眼裡世間存在的萬物皆為對象,不存在的也可以創造出來。對象是特征和技能的結合,其中特征和技能分别對應對象的資料屬性和方法屬性。
優點是:解決了程式的擴充性。對某一個對象單獨修改,會立刻反映到整個體系中,如對遊戲中一個人物參數的特征和技能修改都很容易。
缺點:可控性差,無法向面向過程的程式設計流水線式的可以很精準的預測問題的處理流程與結果,面向對象的程式一旦開始就由對象之間的互動解決問題,即便是上帝也無法預測最終結果。于是我們經常看到一個遊戲人某一參數的修改極有可能導緻陰霸的技能出現,一刀砍死3個人,這個遊戲就失去平衡。
應用場景:需求經常變化的軟體,一般需求的變化都集中在使用者層,網際網路應用,企業内部軟體,遊戲等都是面向對象的程式設計大顯身手的好地方。
2、id、type和value的概念
在python當中一切皆對象,每産生一個對象會對應三個屬性:id、類型type和數值
id可以了解為在記憶體當中的位置(其實不是,id實際指向的是對象的位址)
is是身份運算符,其中id對應的就是身份。
id相同,數值肯定相同;id不相同,數值一定不相同嗎?不是。
代碼示例:
#!/usr/bin/python
# -*- coding:utf-8 -*-
x = 10
print(id(x))
print(type(x))
print(x)
y = 10
print(id(y))
print(type(y))
print(y)
#判斷x和y的記憶體位址是否相同
print(x is y)
#判斷x和y的數值是否相同
print(x == 7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
運作結果:
1547159920
10
1547159920
10
True
False
Process finished with exit code 0
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
代碼示例:(不是在PyCharm當中操作的)
>>> x = 300
>>> y = 300(
>>> id(x)
6368784
>>> id(y)
7343856
>>> x == y
1
2
3
4
5
6
7
1
2
3
4
5
6
7
3、類和對象的概念
1>把一類事物的靜态屬性和動态可以執行的操作組合在一起所得到的這個概念就是類
2>類的一個個體就是對象,對象是具體的,實實在在的事物
3>對象是特征與技能的結合體,其中特征和技能分别對應對象的資料屬性和方法屬性
4>對象(執行個體)本身隻有資料屬性,但是Python的class機制會将類的函數綁定到對象上,稱為對象的方法,或者叫綁定方法,綁定方法唯一綁定一個對象,同一個類的方法綁定到不同的對象上,屬于不同的方法,記憶體位址都不會一樣
在類内部定義的屬性屬于類本身的,由作業系統隻配置設定一塊記憶體空間,大家公用這一塊記憶體空間
5>建立一個類就會建立一個類的名稱空間,用來存儲類中定義的所有名字,這些名字稱為類的屬性:而類中有兩種屬性:資料屬性和函數屬性,其中類的資料屬性是共享給所有對象的,而類的函數屬性是綁定到所有對象的。
6>建立一個對象(執行個體)就會建立一個對象(執行個體)的名稱空間,存放對象(執行個體)的名字,稱為對象(執行個體)的屬性
7>在obj.name會先從obj自己的名稱空間裡找name,找不到則去類中找,類也找不到就找父類…最後都找不到就抛出異常。
8>類的相關方法:
類的相關方法(定義一個類,也會産生自己的名稱空間)
類名.__name__ # 類的名字(字元串)
類名.__doc__ # 類的文檔字元串
類名.__base__ # 類的第一個父類(在講繼承時會講)
類名.__bases__ # 類所有父類構成的元組(在講繼承時會講)
類名.__dict__ # 類的字典屬性、名稱空間
類名.__module__ # 類定義所在的子產品
類名.__class__ # 執行個體對應的類(僅新式類中)
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
9>其餘概念:
1.建立出類會産生名稱空間,執行個體化對象也會産生名稱空間。
2.使用者自己定義的一個類,實際上就是定義了一個類型,類型與類是統一的。
3.使用者先是從自己的命名空間找,如果找不大,在從類的命名空間找。
student1.langage = "1111"
print(student1.__dict__) ===>先是從自己的命名空間找
print(Student.__dict__) ===>然後在從類的命名空間找
4.通過類來通路,通路的是函數,通過對象來通路,通路的是方法,在類内部定義的方式實際上是綁定到對象的身上來用的。
>
>
>
5.總結:類的資料屬性是大家共有的,而且大家的内部位址是一樣的,用的就是一個
類的函數屬性是綁定到大家身上的,内部位址不一樣,綁定方法指的是綁定到對象身上。
綁定方法:綁定到誰的身上,就是給誰用的,誰來調用就會自動把自己當做第一個參數傳入。
**定義在類内部的變量,是所有對象共有的,id全一樣,
**定義在類内部的函數,是綁定到所有對象的,是給對象來用的,obj.fun()會把obj本身當做
一個參數來傳遞。
6.在類内部定義的函數雖然可以由類來調用,但是并不是為了給類用的,在類内部定義的函數的目的就是為了綁定到對象身上的。
7.在類的内部來說,__init__是類的函數屬性,但是對于對象來說,就是綁定方法。
8.命名空間的問題:先從對象的命名空間找,随後在從類的命名空間找,随後在從父類的命名
空間找。
print(student1.x)
9.在定義類的時候,可以想什麼先寫什麼。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
示例程式1:編寫一個學生類,産生一堆學生對象,要求有一個計數器(屬性),統計總共執行個體了多少個對象。
#!/usr/bin/python
# -*- coding:utf-8 -*-
class Student():
#在類内部定義的屬性屬于類本身的,由作業系統隻配置設定一塊記憶體空間,大家公用這一塊記憶體空間。
count = 0
def __init__(self,name,age):
self.name = name
self.age = age
Student.count +=1
if __name__ == '__main__':
student1 = Student("lidong",25)
print(student1.__dict__)
student2 = Student("wangwu",28)
print(student2.__dict__)
print(Student.count)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
運作結果:
{'age': 25, 'name': 'lidong'}
{'age': 28, 'name': 'wangwu'}
2
Process finished with exit code 0
1
2
3
4
5
1
2
3
4
5
示例程式2:(對象之間的互動,重點)
#!/usr/bin/python
# -*- coding:utf-8 -*-
"""
1、什麼叫做抽象方法?含有@abc.abstractmethod辨別符的就是?
2、原樣抄下來也是重寫?
"""
#定義蓋倫類和瑞文類,并進行互相殘血
#對象之間的互動問題(面向對象之間互互相動)
class Garen:
camp = "Demacia"
#定義一個對象的時候,指定了這個對象的生命值和殺傷力
def __init__(self,nickname,life_value=200,aggre_value=100):
self.nickname = nickname
self.life_value = life_value
self.aggre_value = aggre_value
def attack(self,enemy):
enemy.life_value = enemy.life_value - self.aggre_value
class Riven:
camp = "Demacia"
# 定義一個對象的時候,指定了這個對象的生命值和殺傷力
def __init__(self, nickname, life_value=100, aggre_value=200):
self.nickname = nickname
self.life_value = life_value
self.aggre_value = aggre_value
def attack(self, enemy):
#python為弱類型語言
enemy.life_value = enemy.life_value - self.aggre_value
g = Garen("蓋倫")
r = Riven("瑞文")
print("蓋倫的生命值是%s"%g.life_value)
print("瑞文的生命值是%s"%r.life_value)
g.attack(r)
print("瑞文的生命值是%s"%r.life_value)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
運作結果:
蓋倫的生命值是200
瑞文的生命值是100
瑞文的生命值是0
Process finished with exit code 0
1
2
3
4
5
1
2
3
4
5
4、初始化構造函數\__init_的作用
所謂初始化構造函數就是在構造對象的同時被對象自動調用,完成對事物的初始化,一個類隻要生成一個類對象,它一定會調用初始化構造函數.
特點:
1>一個類中隻能有一個初始化構造函數
2>不能有傳回值
3>可以用它來為每個執行個體定制自己的特征
示例程式:
#!/usr/bin/python
# -*- coding:utf-8 -*-
class Student():
def __init__(self,name,age):
self.name = name
self.age = age
print(self.name,self.age)
if __name__ == '__main__':
#在構造對象的時候會自動調用初始化構造函數
student = Student("Alex",100)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
運作結果:
Alex 100
Process finished with exit code 0
1
2
3
1
2
3
5、self關鍵字的用法
為了辨識此時此刻正在處理哪個對象,self指針變量指向目前時刻正在處理的對象,即構造出來的對象
在構造方法中self代表的是:self指針變量指向目前時刻正在建立的對象
構造函數中self.name = name 的含義:将局部變量name的數值發送給目前時刻正在建立的對象中的name成員
示例程式:
#!/usr/bin/python
# -*- coding:utf-8 -*-
class Student():
def __init__(self):
print("目前對象的位址是:%s"%self)
if __name__ == '__main__':
student1 = Student()
student2 = Student()
1
2
3
4
5
6
7
8
9
10
11
1
2
3
4
5
6
7
8
9
10
11
運作結果:
#!/usr/bin/python
# -*- coding:utf-8 -*-
class Student():
def __init__(self):
print("目前對象的位址是:%s"%self)
if __name__ == '__main__':
student1 = Student()
print(student1)
student2 = Student()
print(student2)
1
2
3
4
5
6
7
8
9
10
11
12
13
1
2
3
4
5
6
7
8
9
10
11
12
13
運作結果:
目前對象的位址是:<__main__.Student object at 0x00000000025ACF28>
<__main__.Student object at 0x00000000025ACF28>
目前對象的位址是:<__main__.Student object at 0x00000000025D4048>
<__main__.Student object at 0x00000000025D4048>
Process finished with exit code 0
1
2
3
4
5
6
1
2
3
4
5
6
在上面的程式中,student1、student2、self實際上都是指針變量,存放的是位址,指定目前時刻正在調用的那個對象。
6、繼承的概念
1、一個類從已有的類那裡獲得其已有的屬性與方法,這種現象叫做類的繼承
2、方法重寫指在子類中重新定義父類中已有的方法,這中現象叫做方法的重寫
3、若A類繼承了B類,則aa對象既是A,又是B,繼承反映的是一種誰是誰的關系,隻有在誰是誰的情況下,才能用繼承解決代碼備援的問題。
4、尋找屬性和方法的順序問題:先從對象自己的命名空間中找,然後在自己的類中,最後在從父類當中去找
5、在python3當中,所有的類都是新式,所有的類都直接或者間接的繼承了Object
6、在python中,建立的類可以繼承一個或多個父類
示例代碼1:繼承和方法重寫
#!/usr/bin/python
# -*- coding:utf-8 -*-
class People:
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
def tell(self):
print("%s-%s-%s"%(self.name,self.age,self.sex))
class Student(People):
def __init__(self,name,age,sex,salary):
# self.name = name
# self.age = age
# self.sex = sex
People.__init__(self,name,age,sex)
self.salary = salary
def tell(self):
print("%s是最棒的!"%self.name)
if __name__ == '__main__':
student = Student("alex",20,"man",2000)
student.tell()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
運作結果:
alex是最棒的!
Process finished with exit code 0
1
2
3
1
2
3
代碼示例2:屬性的搜尋順序問題
#!/usr/bin/python
# -*- coding:utf-8 -*-
class People:
def __init__(self,name,age):
self.name = name
self.age = age
self.tell()
def tell(self):
print("%s---%s"%(self.name,self.age))
class Student(People):
def tell(self):
print("呵呵!")
if __name__ == '__main__':
student = Student("alex",20)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
運作結果:
呵呵
1
1
示例代碼3:
#!/usr/bin/python
# -*- coding:utf-8 -*-
class People:
def __init__(self,name,age):
self.name = name
self.age = age
self.tell()
def tell(self):
print("%s---%s"%(self.name,self.age))
class Student(People):
def tell(self):
print("呵呵!")
if __name__ == '__main__':
student = Student("alex",20)
#檢視Student所有的父類
print(Student.__bases__)
#檢視最近的父類
print(Student.__base__)
#student既是Student類,又是People類
print(isinstance(student,Student))
print(isinstance(student,People))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
運作結果:
呵呵!
(,)
True
True
Process finished with exit code 0
1
2
3
4
5
6
7
1
2
3
4
5
6
7
7、組合的概念
1、一個類的屬性可以是一個類對象,通常情況下在一個類裡面很少定義一個對象就是它本身,實際意義很少
2、将另外一個對象作為自己的屬性成員(自己的一個屬性來自于另外一個對象),這就是組合
3、組合也可以解決代碼備援的問題,但是組合反應的是一種什麼是什麼的關系。
示例代碼1:
#!/usr/bin/python
# -*- coding:utf-8 -*-
class Date:
def __init__(self,year,month,day):
self.year = year
self.month = month
self.day = day
def tell(self):
print("%s--%s--%s"%(self.year,self.month,self.day))
class People:
def __init__(self,name,age):
self.name = name
self.age = age
class Student(People):
def __init__(self,name,age,sex,year,month,day):
People.__init__(self,name,age)
self.sex = sex
#下面這一步驟就是組合
self.birth = Date(year,month,day)
if __name__ == '__main__':
student = Student("alex",25,"man",2015,12,31)
print("student的birth成員指向了一個Date對象!")
print("%s"%student.birth)
student.birth.tell()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
運作結果:
student的birth成員指向了一個Date對象!
<__main__.Date object at 0x0000000002604358>
2015--12--31
Process finished with exit code 0
1
2
3
4
5
1
2
3
4
5
8、接口的概念
1、通過接口可以實作不相關類的相同行為,可以起到一個标志的作用.
2、接口提供了不同的類進行互相協作的平台
3、在python中根本就沒有一個叫做interface的關鍵字,如果非要去模仿接口的概念,可以借助第三方子產品
4、raise:主動抛出異常,本來沒有錯,主動抛出錯。
raise TypeError(“類型錯誤”)
示例程式:
#!/usr/bin/python
# -*- coding:utf-8 -*-
#模拟Java中接口的概念
class S1:
def read(self):
raise TypeError("類型錯誤")
def write(self):
raise TypeError("類型錯誤")
class S2(S1):
def read(self):
print("from S2")
def write(self):
print("from S2")
class S3(S1):
def read(self):
print("from S3")
def read(self):
print("from S3")
if __name__ == '__main__':
s2 = S2()
s2.read()
s3 = S3()
s3.read()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
運作結果:
from S2
from S3
Process finished with exit code 0
1
2
3
4
1
2
3
4
在上面的程式中存在着一個問題,在S2和S3中如果不實作read和write方法,仍然可以執行個體化,如何解決,看抽象類的概念。
9、抽象類的概念
抽象類是為了更好的對類加以分類,抽象類通常情況下是作為一個類族的最頂層的父類,如植物,并用最底層的類來描述現實世界中的具體的事物.
1>Python中抽象方法定義的方式:利用abc子產品實作抽象類,在Java當中如果一個方法沒有執行體就叫做抽象方法,而在Python中不是以執行體的有無作為标準,而是以一個方法是否有@abc.abstractmethod裝飾器作為标準,有則是抽象方法
2>抽象方法通過子類的實作可以變成普通的方法
3>抽象方法不存在所謂重寫的問題,卻存在着實作的問題
4>含有抽象方法的類一定是抽象類,但是抽象類不一定含有抽象方法,此時也就沒有什麼意義了
5>抽象類是一個介于類和接口直接的一個概念,同時具備類和接口的部分特性,可以用來實作歸一化設計
示例程式:
#!/usr/bin/python
# -*- coding:utf-8 -*-
"""
1、什麼叫做抽象方法?含有@abc.abstractmethod辨別符的就是?
2、原樣抄下來也是重寫?
"""
import abc
class File(metaclass=abc.ABCMeta):
@abc.abstractmethod
def read(self):
pass
#抽象類中可以有普通方法
def write(self):
print("11111")
class B(File):
#如果寫pass,也是可以的,此時子類将會覆寫掉父類
def read(self):
pass
if __name__ == '__main__':
bb = B()
bb.read()
bb.write()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
運作結果:
11111
Process finished with exit code 0
1
2
3
4
1
2
3
4
10、屬性與方法的周遊問題(MRO清單)
python到底是如何實作繼承的,對于你定義的每一個類,python會計算出一個方法解析順序(MRO)清單,這個MRO清單就是一個簡單的所有基類的線性順序清單,例如:
>>> F.mro() #等同于F.__mro__
[, , , , , , ]
1
2
1
2
為了實作繼承,python會在MRO清單上從左到右開始查找基類,直到找到第一個比對這個屬性的類為止。
而這個MRO清單的構造是通過一個C3線性化算法來實作的。我們不去深究這個算法的數學原理,它實際上就是合并所有父類的MRO清單并遵循如下三條準則:
1.子類會先于父類被檢查
2.多個父類會根據它們在清單中的順序被檢查
3.如果對下一個類存在兩個合法的選擇,選擇第一個父類
示例程式:
#!/usr/bin/python
# -*- coding:utf-8 -*-
"""
1、什麼叫做抽象方法?含有@abc.abstractmethod辨別符的就是?
2、原樣抄下來也是重寫?
"""
class A:
def fun(self):
print("aaaa")
class B(A):
def fun(self):
print("bbbb")
class C:
def fun(self):
print("cccc")
class D(C):
def fun(self):
print("dddd")
class F(B,D):
def fun(self):
print("ffff")
if __name__ == '__main__':
print(F.mro())
#F==>B==>A==>D==>C===>Object
ff = F()
ff.fun()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
運作結果:
[, , , , , ]
ffff
Process finished with exit code 0
1
2
3
4
5
1
2
3
4
5
11、super關鍵字的使用
1、super關鍵字産生的原因:在子類當中可以通過使用super關鍵字來調用父類的中相應的方法,簡化代碼。
2、使用super調用的所有屬性,都是從MRO清單目前的位置往後找,千萬不要通過看代碼去找繼承關系,一定要看MRO清單。
示例代碼:
#!/usr/bin/python
# -*- coding:utf-8 -*-
class Foo:
def test(self):
print("from foo")
class Bar(Foo):
def test(self):
#Foo.test(self)
super().test()
print("bar")
if __name__ == '__main__':
bb = Bar()
bb.test()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
運作結果:
from foo
bar
Process finished with exit code 0
1
2
3
4
1
2
3
4
12、多态的概念
1、所謂多态指的是一個父類的引用既可以指向父類的對象,也可以指向子類的對象,它可以根據目前時刻指向的不同,自動調用不同對象的方法,這就是多态的概念。(當然,Python中的多态沒必要了解的這麼複雜,因為Python自帶多态的性能)
2、多态性依賴于同一種事物的不同種形态
3、Python是一門弱類型的語言,所謂弱類型語言指的是對參數沒有類型限制,而這是我們可以随意傳入對象的根本原因
示例程式:
#!/usr/bin/python
# -*- coding:utf-8 -*-
#模拟Java中接口的概念
class People:
def fun(self):
print("1111")
class Student(People):
def fun(self):
print("2222")
class Teacher(People):
def fun(self):
print("3333")
def g(aa):
aa.fun()
if __name__ == '__main__':
print("隻要傳入的對象是people的子類即可")
g(People())
g(Student())
g(Teacher())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
運作結果:
隻要傳入的對象是people的子類即可
1111
2222
3333
Process finished with exit code 0
1
2
3
4
5
6
1
2
3
4
5
6
13、封裝的概念
1、在面向對象中,所有的類通常情況下很少讓外部類直接通路類内部的屬性和方法,而是向外部類提供一些按鈕,對其内部的成員進行通路,以保證程式的安全性,這就是封裝
2、在python中用雙下劃線的方式實作隐藏屬性,即實作封裝
3、通路控制符的用法___包括兩種:在類的内部與在類的外部
1>在一個類的内部,所有的成員之間彼此之間都可以進行互相通路,通路控制符__是透明的,失效的
2>在一個類的外部,通過_類名_對象的方式才可以通路到對象中的_成員
綜上:内部之間可以直接通路,在類的外部必須換一種文法方式進行通路
4、在python當中如何實作一個隐藏的效果呢?答案:在Python裡面沒有真正意義上的隐藏,隻能
從文法級别去實作這件事。
5、在子類定義的__x不會覆寫在父類定義的__x,因為子類中變形成了:子類名__x,而父類中變形成了:父類名__x,即雙下滑線開頭的屬性在繼承給子類時,子類是無法覆寫的
示例程式1:
#!/usr/bin/python
# -*- coding:utf-8 -*-
class Student:
def __init__(self,name,age):
self.__name = name
self.__age = age
def setter(self,name,age):
if not isinstance(name,str):
raise TypeError("名字必須是字元串類型")
if not isinstance(age,int):
raise TypeError("年齡必須是整數類型")
self.__name = name
self.__age = age
def tell(self):
print("學生的資訊是:%s\t%s"%(self.__name,self.__age))
if __name__ == '__main__':
student = Student("Alex",25)
student.tell()
student.setter("Alex_sb",40)
student.tell()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
運作結果:
學生的資訊是:Alex 25
學生的資訊是:Alex_sb 40
Process finished with exit code 0
1
2
3
4
1
2
3
4
14、@property的用法
1、property是一種特殊的屬性,通路它時會執行一段功能(函數)然後傳回值
2、将一個類的函數定義成特性以後,對象再去使用的時候obj.name,根本無法察覺自己的name是執行了一個函數然後計算出來的,這種特性的使用方式遵循了統一通路的原則(使用的原因)
3、一旦給函數加上一個裝飾器@property,調用函數的時候不用加括号就可以直接調用函數了
代碼示例:
class Student:
@property
def fun(self):
print("1111111")
if __name__ == '__main__':
student = Student()
student.fun
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
示例程式2:
#!/usr/bin/python
# -*- coding:utf-8 -*-
"""
1、什麼叫做抽象方法?含有@abc.abstractmethod辨別符的就是?
2、原樣抄下來也是重寫?
"""
class Student:
def __init__(self,name):
self.__name = name
@property
def name(self):
return self.__name
if __name__ == '__main__':
student = Student("alex")
student._Student__name = "sb_alex"
print(student.name)
###本節需要補
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
15、綁定方法與非綁定方法
綁定方法的使用:
1、在類内部定義的方法,在沒有被任何裝飾器修飾的情況下,就是為了綁定到對象給對象
用的,self關鍵字含有自動傳值的過程,不管寫不寫self關鍵子。
2、預設情況下,在類内部定義的方法都是綁定到對象的方法。
3、綁定方法綁定到誰的身上,誰就作為第一個參數進行傳入。
4、綁定到類的方法給對象使用是沒有任何意義的。
非綁定方法:
statimethod不與類或對象綁定,誰都可以調用,沒有自動傳值效果,python為我們内置了函數staticmethod來把類中的函數定義成靜态方法
不與類或對象綁定,類和對象都可以調用,但是沒有自動傳值那麼一說。就是一個普通工具而已
隻有綁定方法才存在自動傳值的說法。
總結:
綁定到對象的方法,調用的時候會将對象參數自動傳入, ===>方法上面什麼也不加
綁定到類的方法,調用的時候會将類作為參數自動傳入, ===>方法上面加classmethod
非綁定方法不與類或對象綁定,類和對象都可以調用,但是沒有自動傳值那麼一說。===>static
使用場景:
看使用什麼調用?類對象or類or什麼參數都不需要.
模拟資料庫登陸的場景。
(隻有類才有執行個體化的說法)
建立資料庫的時候加上一個id屬性,指定是哪一個連結。
每次資料庫執行個體話的時候都要指派一個id。
示例程式1:
#!/usr/bin/python
# -*- coding:utf-8 -*-
#造成id的方式,用hash算法
import time
import hashlib
def create_id():
m = hashlib.md5(str(time.clock()).encode("utf-8"))
return m.hexdigest()
#time.clock()計算的是cpu真實的時間
print(create_id())
print(create_id())
print(create_id())
print(create_id())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
運作結果:
3a92235c44873cbcf618a132a2781157
aa0968a1f467ba19f1595c70efb2c3ee
abc4e2f842e0283b2186d459c069d301
28e76bcb1724833a847851e8f2286e65
Process finished with exit code 0
1
2
3
4
5
6
1
2
3
4
5
6
示例程式2:
#!/usr/bin/python
# -*- coding:utf-8 -*-
import settings
import time
import hashlib
class MySQL:
def __init__(self,host, port):
self.id = self.create_id()
self.host = host
self.port = port
print("connecting.....")
def select(self): # 綁定到對象的方法
print(self)
print("select function")
# 綁定到類的方法,從配置檔案中擷取主機名和端口号,使用者預設連結資料庫的一種方式
@classmethod
def from_conf(cls):
# 執行個體話的結果得到了一個類對象
# 通過綁定對象的方法間接的去建立了一個類對象
return cls(settings.HOST, settings.PORT) # 相當于MySQL("127.1.1.1",3306)
#工具包既不依賴于類,也不依賴于對象
#非綁定方法就是類中普通的工具包,不依賴于self和cls的參數
@staticmethod
def create_id():
m = hashlib.md5(str(time.clock()).encode("utf-8"))
return m.hexdigest()
if __name__ == '__main__':
conn = MySQL("192.168.80.100", 3306)
conn.select()
conn2 = MySQL.from_conf()
conn2.select()
print(conn.id)
print(conn2.id)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
運作結果:
connecting.....
<__main__.MySQL object at 0x0000000002584438>
select function
connecting.....
<__main__.MySQL object at 0x0000000002584EB8>
select function
3a92235c44873cbcf618a132a2781157
294e6de59d0e8d4a75717d5bb20f92e0
Process finished with exit code 0
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
16、staticmethod與classmethod的差別
staticmethod與classmethod的差別:前者是非綁定方法,後者是綁定到類的方法
示例程式1:
#!/usr/bin/python
# -*- coding:utf-8 -*-
import settings
import time
import hashlib
class MySQL:
def __init__(self,host, port):
self.host = host
self.port = port
print("connecting.....")
@staticmethod
def from_conf():
return MySQL(settings.HOST,settings.PORT) #相當于MySQL("127.1.1.1",3306)
def __str__(self):
return "父類"
class Mariab(MySQL):
def __str__(self):
return "子類"
if __name__ == '__main__':
conn = MySQL.from_conf()
print(conn.host)
conn1 = Mariab.from_conf()
print(conn1.host)
#本來想擷取Mariab的一個對象,但是現在擷取的是MySQL的一個對象,這是子類繼承的一個問題
print(conn1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
運作結果:
connecting.....
127.1.1.1
connecting.....
127.1.1.1
父類
Process finished with exit code 0
1
2
3
4
5
6
7
1
2
3
4
5
6
7
示例程式2:基于1的改進
#!/usr/bin/python
# -*- coding:utf-8 -*-
import settings
import time
import hashlib
class MySQL:
def __init__(self,host, port):
self.host = host
self.port = port
print("connecting.....")
@classmethod
def from_conf(cls):
return cls(settings.HOST,settings.PORT) #相當于MySQL("127.1.1.1",3306)
def __str__(self):
return "父類"
class Mariab(MySQL):
def __str__(self):
return "子類"
if __name__ == '__main__':
conn = MySQL.from_conf()
print(conn.host)
conn1 = Mariab.from_conf()
print(conn1.host)
#本來想擷取Mariab的一個對象,但是現在擷取的是MySQL的一個對象,這是子類繼承的一個問題
print(conn1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
運作結果:
connecting.....
127.1.1.1
connecting.....
127.1.1.1
子類
Process finished with exit code 0
1
2
3
4
5
6
7
1
2
3
4
5
6
7
17、總和應用的一個小例子
要求:
定義MySQL類
1.對象有id、host、port三個屬性
2.定義工具create_id,在執行個體化時為每個對象随機生成id,保證id唯一
3.提供兩種執行個體化方式,方式一:使用者傳入host和port 方式二:從配置檔案中讀取host和port進行執行個體化
4.為對象定制方法,save和get,save能自動将對象序列化到檔案中,檔案名為id号,檔案路徑為配置檔案中DB_PATH;get方法用來從檔案中反序列化出對象。
代碼示例:
#!/usr/bin/python
# -*- coding:utf-8 -*-
import time
import hashlib
import settings
import random
import pickle
import os
"""
HOST = "127.1.1.1"
PORT = 3306
DB_PATH = r"D:\Python Work Location\Python 0507\day07\db"
"""
class MySQL:
@staticmethod
def create_id():
m = hashlib.md5(str(time.clock()).encode("utf-8"))
return m.hexdigest()
def __init__(self,host,port):
#為每一個對象建立了一個ID
self.id = self.create_id()
self.host = host
self.port = port
#從配置檔案中讀取在這裡用到了classmethod
@classmethod
def from_conf(cls):
return cls(settings.HOST,settings.PORT)
def save(self):
file_path = r"%s%s%s"%(settings.DB_PATH,os.sep,self.id)
#将這個對象以二進制的形式寫到硬碟當中
pickle.dump(self,open(file_path,"wb"))
def get(self):
#在這裡面通過id的方式保證了檔案的名字是唯一的
file_path = r"%s%s%s" % (settings.DB_PATH, os.sep, self.id)
return pickle.load(open(file_path,"rb"))
#對于Python中的對象json無法進行序列化
if __name__ == '__main__':
conn1 = MySQL("172.1.2.1","3306")
print(conn1.id)
conn1.save()
result = conn1.get()
print(result.id)
#通過os.listdir指令可以浏覽某一個目錄下面有哪些檔案,然後循環的反序列化
print(os.listdir(r"D:\Python Work Location\Python 0507\day07\db"))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
運作結果:
85b8a467159e14ec4b2d16bff39ed199
85b8a467159e14ec4b2d16bff39ed199
['85b8a467159e14ec4b2d16bff39ed199', 'd0cff574feed705df3655a209c79c7ef']
Process finished with exit code 0