第一章 面向對象初識
面向對象的三大特性是什么? 抽象、繼承、多態。
面向對象第一個優點:*
對相似功能的函數,同一個業務的函數進行歸類,分類,使你的代碼更清晰化,更合理化。
什么是面向對象。
面向對象的程序設計的核心是對象(上帝式思維),要理解對象為何物,必須把自己當成上帝,上帝眼里世間存在的萬物皆為對象,不存在的也可以創造出來。
那什么是類?什么是對象?
類:就是具有相同屬性和功能的一類事物。
對象:就是類的具體表現。對象一定是不一樣的
面向對象的第二個優點:
面向對象,要擁有上帝的視角看問題,類其實就是一個公共模板(廠房),對象就是從具體的模板實例化出來,得對象,得一切
1. 面向對象的結構
class Human:
"""
類的具體結構
"""
#第一部分:靜態屬性
mind = "思想"
language ="語言"
#第二部分:動態方法
def work(self):
print("工作")
def eat(self):
print("吃飯")
class 是關鍵字與def用法相同,定義一個類。
Human是此類的類名,類名使用駝峰(CamelCase)命名風格,首字母大寫,私有類可用一個下划線開頭。
類的結構從大方向來說就分為兩部分:
靜態變量。
動態方法。
1. 從類名的角度研究類
1.類名操作類中的屬性
class Human:
"""
類的具體結構
"""
#第一部分:靜態屬性
mind = "思想"
language ="語言"
#第二部分:動態方法
def work(self):
print("工作")
def eat(self):
print("吃飯")
1.類名查看類中所有的內容
print(Human.__dict__)
2.類名操作類中的靜態屬性
1.增加
Human.body="有頭發"
print(Human.__dict__)
2.刪除
del Human.mind
3.改
Human.mind ="個性"
print(Human.__dict__)
4.查
print(Human.language)
# print(Human.__dict__)
2.類名操作動態方法
除了兩個特殊方法:靜態方法,類方法之外,一般不會通過類名操作一個類中的方法.
Human.work(123)
總結:一般類名
3. 從對象的角度研究類
1.什么是對象
對象是從類中出來的,只要是類名加上(),這就是一個實例化過程,這個就會實例化一個對象。**
實例化一個對象總共發生了三件事:
1,在內存中開辟了一個對象空間。
2,自動執行類中的____init____方法,並將這個對象空間(內存地址)傳給了__init__方法的第一個位置參數self。
3,在____init__ _方法中通過self給對象空間添加屬性。
class Human:
"""
類的具體結構
"""
mind = "思想"
language ="語言"
def __init__(self,name,age):
# self 和 obj 指向的是同一個內存地址同一個空間,下面就是通過self給這個對象空間封裝四個屬性。
self.n=name
self.a=age
def work(self):
print("工作")
def eat(self):
print("吃飯")
obj=Human("zbb",18) #實例化過程
#得到一個返回值,這個返回值就是對象,實例
print(obj.n)
print(obj.a)
print(obj.__dict__)
#zbb
#{'n': 'zbb'}
2.對象操作對象空間屬性
1.對象查看對象的空間的所有屬性
obj = Human('zbb',18)
print(obj.__dict__)
2. 對象操作對象空間的屬性
obj = Human('zbb',18)
增:
obj.sex = 'laddy_boy'
刪:
del obj.a
改:
obj.a = 1000
查:
print(obj.n)
print(obj.__dict__)
3.對象查看類中的屬性
obj = Human('zbb',18)
# print(obj.mind)
obj.mind = '無腦的'
print(obj.mind)
print(Human.mind)
4.對象調用類中的方法
class Human:
mind = '有思想'
language = '實用語言'
def __init__(self,name,sex,age,hobby):
self.n = name
self.s = sex
self.a = age
self.h = hobby
def work(self):
print(self)
print('人類會工作')
def tools(self):
print('人類會使用工具')
obj = Human('barry','男',18,'運動')
obj.work()
obj.tools()
5.self 是什么?
self其實就是類中方法(函數)的第一個位置參數,只不過解釋器會自動將調用這個函數的對象傳給self。所以咱們把類中的方法的第一個參數約定俗成設置成self, 代表這個就是對象。
一個類可以實例化多個對象
obj1= Human('小胖','男',20,'美女')
obj2= Human('相爺','男',18,'肥女')
print(obj1,obj2)
print(obj1.__dict__)
print(obj2.__dict__)
第二章 類空間問題以及類之間的關系
1.何處可以添加對象屬性
class A:
def __init__(self,name):
self.name = name
def func(self,sex):
self.sex = sex
# 類外面可以:
obj = A('barry')
obj.age = 18
print(obj.__dict__) # {'name': 'barry', 'age': 18}
# 類內部也可以:
obj = A('barry') # __init__方法可以。
obj.func('男') # func 方法也可以。
2.何處可以添加類的靜態屬性
class A:
def __init__(self, name):
self.name = name
def func(self, sex):
self.sex = sex
def func1(self):
A.bbb = 'ccc'
# 類的外部可以添加
A.aaa = 'djb'
print(A.__dict__)
# 類的內部也可以添加。
A.func1(111)
print(A.__dict__)
3. 對象如何找到類的屬性
對象查找屬性的順序:先從對象空間找 ------> 類空間找 ------> 父類空間找 ------->.....
類名查找屬性的順序:先從本類空間找 -------> 父類空間找--------> ........
上面的順序都是單向不可逆,類名不可能找到對象的屬性。
4.類與類之間的關系
- 依賴關系
- 關聯關系
- 組合關系
- 聚合關系
- 實現關系
- 繼承關系(類的三大特性之一:繼承。)
1.依賴關系
主從之分
依賴關系:將一個類的對象或者類名傳到另一個類的方法使用。
2.關聯,組合,聚合關系
- 關聯關系. 兩種事物必須是互相關聯的. 但是在某些特殊情況下是可以更改和更換的.
- 聚合關系. 屬於關聯關系中的⼀種特例. 側重點是xxx和xxx聚合成xxx. 各⾃有各⾃的聲明周期. 比如電腦. 電腦⾥有CPU, 硬盤, 內存等等. 電腦掛了. CPU還是好的. 還是完整的個體
- 組合關系. 給對象封裝一個屬性,屬性值是另一個類的對象
class Boy:
def __init__(self,name,girl=None):
self.name = name
self.girl=girl
def hava_diner(self):
if self.girl:
print(f"{self.name}請{self.girl}吃飯")
else:
print("單身狗")
live=Boy("qw")
# live.hava_diner()
live.girl="zxy"
live.hava_diner()
class Boy:
def __init__(self,name,girl=None):
self.name = name
self.girl=girl
def hava_diner(self):
if self.girl:
print(f"{self.name}請{self.girl}吃飯")
else:
print("單身狗")
def girl_skill(self):
self.girl.skill()
class Girl:
def __init__(self,name):
self.name=name
def skill(self):
print(f"{self.name}能吃")
zbb=Boy("zbb")
# live.hava_diner()
zxy =Girl("zxy")
zbb.girl=zxy
zbb.girl_skill()
class Game_role:
def __init__(self,name,ad,hp):
self.name=name
self.ad=ad
self.hp=hp
def equipment(self,wea):
self.weapon =wea #組合關系
class Weapon:
def __init__(self,name,ad):
self.name = name
self.ad = ad
def weapon_attack(self,p1,p2): #依賴關系
p2.hp-=self.ad
print(f"{p1.name}利用{self.name}給了{p2.name}一槍,{p2.name}掉了{self.ad}血,還剩{p2.hp}")
gailun =Game_role("蓋倫",10,100)
yasuo =Game_role("劍豪",20,80)
Sword = Weapon("大保健",16)
Musket = Weapon('菊花槍',20)
# 給游戲人物封裝武器屬性
gailun.equipment(Sword)
gailun.weapon.weapon_attack(gailun,yasuo)
第三章 繼承
1.什么是面向對象的繼承
比較官方的說法就是:
繼承(英語:inheritance)是面向對象軟件技術當中的一個概念。如果一個類別A“繼承自”另一個類別B,就把這個A稱為“B的子類別”,而把B稱為“A的父類別”也可以稱“B是A的超類”。繼承可以使得子類別具有父類別的各種屬性和方法,而不需要再次編寫相同的代碼。在令子類別繼承父類別的同時,可以重新定義某些屬性,並重寫某些方法,即覆蓋父類別的原有屬性和方法,使其獲得與父類別不同的功能。另外,為子類別追加新的屬性和方法也是常見的做法。 一般靜態的面向對象編程語言,繼承屬於靜態的,意即在子類別的行為在編譯期就已經決定,無法在執行期擴充
專業說法: 如果B類繼承A類
B類就稱為子類,派生類
A類稱為父類,基類,超類
通俗一點就是 子承父業
# 繼承的用法:
class Aniaml:
live = "123"
def __init__(self,name,sex,age):
self.name = name
self.age = age
self.sex = sex
class Cat(Aniaml):
pass
class Dog(Aniaml):
pass
優點: 減少重復代碼 .
增加類之間的耦合度(耦合性不宜多,宜精)
2.繼承的分類
在python2x版本中存在兩種類.:
⼀個叫經典類. 在python2.2之前. ⼀直使⽤的是經典類. 經典類在基類的根如果什么都不寫.
⼀個叫新式類. 在python2.2之后出現了新式類. 新式類的特點是基類的根是object類。
python3x版本中只有一種類:
python3中使⽤的都是新式類. 如果基類誰都不繼承. 那這個類會默認繼承 object
3.單繼承
1.類名,對象執行父類的方法
class Animal:
live = "有生命"
def __init__(self,name,sex,age):
self.name =name
self.sex =sex
self.age =age
def eat(self):
print("chi")
class Human(Animal):
body = "有思想"
#類名執行父類屬性的方法(不常用)
print(Human.live)
Human.eat(1213)
#子類執行父類的方法
obj = Human("zbb","男","23") #相當於實例化
print(obj.live)
2.執行順序
p1 = Person('barry','男',18)
# 實例化對象時必須執行__init__方法,類中沒有,從父類找,父類沒有,從object類中找。
p1.eat()
# 先要執行自己類中的eat方法,自己類沒有才能執行父類中的方法。
3.同時執行子類以及父類方法
第一種方法(不常用)
class Animal:
def eat(self):
print("chi")
class Human:
def __init__(self,name):
self.name =name
def eat(self):
print(f"{self.name}需要吃飯")
Animal.eat(self.name)
obj = Human("zbb")
obj.eat()
第二種方法
class Animal:
def eat(self):
print("chi")
class Human(Animal):
def __init__(self,name):
self.name =name
def eat(self):
print(f"{self.name}需要吃飯")
super().eat()
obj = Human("zbb")
obj.eat()
super() 重構父類的方法
解決多重繼承問題
不需要明確給出任何基類的名字,它會自動找到所有直接基類,及其對應的方法.用於繼承.
super()的好處就是可以避免直接使用父類的名字.主要用於多重繼承
4.多繼承
在python2x版本中存在兩種類.:
⼀個叫經典類. 在python2.2之前. ⼀直使⽤的是經典類. 經典類在基類的根如果什么都不寫.
⼀個叫新式類. 在python2.2之后出現了新式類. 新式類的特點是基類的根是object類。
python3x版本中只有一種類:
python3中使⽤的都是新式類. 如果基類誰都不繼承. 那這個類會默認繼承 object
1.經典類的多繼承
經典類: 深度優先.從左至右,深度優先.
不繼承object類
2.新式類的多繼承
繼承object類
class O:
name = '1'
class D(O):
pass
class E(O):
name = '2'
# pass
class F(O):
name = '3'
class B(D,E):
pass
class C(E,F):
pass
class A(B,C):
pass
obj = A()
print(obj.name)
# mro(Child(Base1,Base2)) = [ Child ] + merge( mro(Base1), mro(Base2), [ Base1, Base2] )
# mro(A(B,C)) = [A] + merge(mro(B),mro(C),[B,C])
總結:直接print 不需要什么算法
print(A.mro())
mro是繼承付父類方法時的順序表
第四章 封裝
封裝,顧名思義就是將內容封裝到某個地方,以后再去調用被封裝在某處的內容。
所以,在使用面向對象的封裝特性時,需要:
- 將內容封裝到某處
- 從某處調用被封裝的內容
第一步:將內容封裝到某處
self 是一個形式參數,當執行 obj1 = Foo( ) 時,self 等於 obj1
第二步:從某處調用被封裝的內容
調用被封裝的內容時,有兩種情況:
- 通過對象直接調用
- 通過self間接調用
1、通過對象直接調用被封裝的內容
上圖展示了對象 ojb1在內存中保存的方式,根據保存格式可以如此調用被封裝的內容:對象.屬性名
class Foo:
def __init__(self,name,age):
self.name = name
self.age = age
obj1 = Foo("zbb",18)
print(obj1.name) # 直接調用obj1對象的name屬性
print(obj1.age) # 直接調用obj1對象的age屬性
2、通過self間接調用被封裝的內容
執行類中的方法時,需要通過self間接調用被封裝的內容
class Foo:
def __init__(self,name,age):
self.name = name
self.age = age
def func(self):
print(self.name)
print(self.age)
obj1 = Foo("zbb",18)
obj1.func() ## Python默認會將obj1傳給self參數
綜上所述,對於面向對象的封裝來說,其實就是使用構造方法將內容封裝到 對象 中,然后通過對象直接或者self間接獲取被封裝的內容。
第五章 多態
多態,同一個對象,多種形態。python默認支持多態。
鴨子類型
python中有一句諺語說的好,你看起來像鴨子,那么你就是鴨子。
對於代碼上的解釋其實很簡答:
class A:
def f1(self):
print('in A f1')
def f2(self):
print('in A f2')
class B:
def f1(self):
print('in A f1')
def f2(self):
print('in A f2')
obj = A()
obj.f1()
obj.f2()
obj2 = B()
obj2.f1()
obj2.f2()
# A 和 B兩個類完全沒有耦合性,但是在某種意義上他們卻統一了一個標准。
# 對相同的功能設定了相同的名字,這樣方便開發,這兩個方法就可以互成為鴨子類型。
# 這樣的例子比比皆是:str tuple list 都有 index方法,這就是統一了規范。
# str bytes 等等 這就是互稱為鴨子類型。
鴨子類型
第六章 類的約束
class Payment:
def pay(self,money):
raise Exception("你的子類需要定義pay方法")
#pass
class QQpay(Payment):
def pay(self,money):
print(f"使用qq支付了{money}")
class Alipay(Payment):
def pay(self,money):
print(f"使用阿里支付了{money}")
class Wechat(Payment):
def fuqian(self,money):
print(f"使用微信支付了{money}")
def pay(obj,money): #統一接口
obj.pay(money) # 這個函數就是統一支付規則,這個叫做: 歸一化設計。
obj1 = QQpay()
obj2 = Alipay()
obj3 = Wechat()
pay(obj1,100)
pay(obj2,200)
# pay(obj3,200) # 會直接找到父類 並發出錯誤
from abc import ABCMeta,abstractmethod
class Payment(metaclass=ABCMeta):
#抽象類 接口類 規范和約束 metaclass指定的是一個元類
@abstractmethod
def pay(self,money):
pass #抽象方法
class QQpay(Payment):
def pay(self,money):
print(f"使用qq支付了{money}")
class Alipay(Payment):
def pay(self,money):
print(f"使用阿里支付了{money}")
class Wechat(Payment):
def fuqian(self,money):
print(f"使用微信支付了{money}")
# def pay(self,money):#強制定義 不定義會報錯
# pass
def pay(obj,money): #統一接口
obj.pay(money)
obj3 = Wechat() #實例化過程 就報錯了
總結: 約束. 其實就是⽗類對⼦類進⾏約束. ⼦類必須要寫xxx⽅法. 在python中約束的⽅式和⽅法有兩種:
1. 使⽤抽象類和抽象⽅法, 由於該⽅案來源是java和c#. 所以使⽤頻率還是很少的
2.使⽤⼈為拋出異常的⽅案. 並且盡量拋出的是NotImplementError. 這樣比較專業, ⽽且錯誤比較明確.(推薦)
第七章 super()深入了解
super是嚴格按照類的繼承順序執行!!!
# super可以下一個類的其他方法
# 嚴格按照類的mro順序執行
class A:
def f1(self):
print('in A')
class Foo(A):
def f1(self):
super().f1()
print('in Foo')
class Bar(A):
def f1(self):
print('in Bar')
class Info(Foo,Bar):
def f1(self):
super(Foo,self).f1()
#super(Info,self).f1() #()默認是
print('in Info f1')
obj = Info()
obj.f1()
第八章 帶顏色的print
書寫格式: 開頭部分:\033[顯示方式;前景色;背景色m + 結尾部分:\033[0m
注意:開頭部分的三個參數:顯示方式,前景色,背景色是可選參數,可以只寫其中的某一個;另外由於表示三個參數不同含義的數值都是唯一的沒有重復的,所以三個參數的書寫先后順序沒有固定要求,系統都能識別;但是,建議按照默認的格式規范書寫。
對於結尾部分,其實也可以省略,但是為了書寫規范,建議\033[***開頭,\033[0m結尾。
-------------------------------------------
-------------------------------------------
字體色 | 背景色 | 顏色描述
-------------------------------------------
30 | 40 | 黑色
31 | 41 | 紅色
32 | 42 | 綠色
33 | 43 | 黃色
34 | 44 | 藍色
35 | 45 | 紫紅色
36 | 46 | 青藍色
37 | 47 | 白色
-------------------------------------------
-------------------------------
顯示方式 | 效果
-------------------------------
0 | 終端默認設置
1 | 高亮顯示
4 | 使用下划線
5 | 閃爍
7 | 反白顯示
8 | 不可見
-------------------------------
數值表示的參數含義:
顯示方式: 0(默認值)、1(高亮)、22(非粗體)、4(下划線)、24(非下划線)、 5(閃爍)、25(非閃爍)、7(反顯)、27(非反顯)
前景色: 30(黑色)、31(紅色)、32(綠色)、 33(黃色)、34(藍色)、35(洋 紅)、36(青色)、37(白色)
背景色: 40(黑色)、41(紅色)、42(綠色)、 43(黃色)、44(藍色)、45(洋 紅)、46(青色)、47(白色)
常見開頭格式:
\033[0m 默認字體正常顯示,不高亮
\033[32;0m 紅色字體正常顯示
\033[1;32;40m 顯示方式: 高亮 字體前景色:綠色 背景色:黑色
\033[0;31;46m 顯示方式: 正常 字體前景色:紅色 背景色:青色
舉例說明:
1,
print('\033[1;35;0m字體變色,但無背景色 \033[0m') # 有高亮 或者 print('\033[1;35m字體有色,但無背景色 \033[0m')
print('\033[1;45m 字體不變色,有背景色 \033[0m') # 有高亮
print('\033[1;35;46m 字體有色,且有背景色 \033[0m') # 有高亮
print('\033[0;35;46m 字體有色,且有背景色 \033[0m') # 無高亮
第九章 類的成員
1.細分類的組成成員
class A:
company_name = 'zbb' # 靜態變量(靜態字段)
__iphone = '1353333xxxx' # 私有靜態變量(私有靜態字段)
def __init__(self,name,age): #特殊方法
self.name = name #對象屬性(普通字段)
self.__age = age # 私有對象屬性(私有普通字段)
def func1(self): # 普通方法
pass
def __func(self): #私有方法
print(666)
@classmethod # 類方法
def class_func(cls):
""" 定義類方法,至少有一個cls參數 """
print('類方法')
@staticmethod #靜態方法
def static_func():
""" 定義靜態方法 ,無默認參數"""
print('靜態方法')
@property # 屬性
def prop(self):
pass
2.類的私有成員
對於每一個類的成員而言都有兩種形式:
- 公有成員,在任何地方都能訪問
- 私有成員,只有在類的內部才能方法
私有成員和公有成員的訪問限制不同:
1.靜態字段(靜態屬性)
公有靜態字段:類可以訪問;類內部可以訪問;派生類中可以訪問
私有靜態字段:僅類內部可以訪問;
class A:
name = 'zbb'
__name = "zmn"
def func(self):
print(self.name)
print(self.__name)
obj = A()
obj.func() #內部可以訪問
外部不能訪問
class A:
name = 'zbb'
__name = "zmn"
def func(self):
pass
obj = A()
print(obj.name)
print(obj.__name) #報錯
print(A.__name) #報錯
2.對象屬性
公有普通字段:對象可以訪問;類內部可以訪問;派生類中可以訪問
私有普通字段:僅類內部可以訪問;
class A:
def __init__(self,name,pwd):
self.name = name
self.__pwd = pwd
def md5(self):
self.__pwd = self.__pwd + "123"
obj = A('zbb',"123")
print(obj.__pwd) ###報錯 不能在類外部以及派生類使用
3.方法:
公有方法:對象可以訪問;類內部可以訪問;派生類中可以訪問
私有方法:僅類內部可以訪問;
class A:
def func(self):
self.__func()
print("1")
def __func(self):
print("2")
obj = A()
obj.func() #內部可以調用
# obj.__func() 報錯
#####print(A._A__func) #(不允許!!!)
總結:
私有成員來說: 當你遇到重要的數據,功能,(只允許本類使用的一些方法,數據)設置成私有成員.
ps:非要訪問私有成員的話,可以通過 對象._類__屬性名,但是絕對不允許!!!*
為什么可以通過._類__私有成員名訪問呢?因為類在創建時,如果遇到了私有成員(包括私有靜態字段,私有普通字段,私有方法)它會將其保存在內存時自動在前面加上_類名.
3.類的其他方法
實例方法
定義:第一個參數必須是實例對象,該參數名一般約定為“self”,通過它來傳遞實例的屬性和方法(也可以傳類的屬性和方法);
調用:只能由實例對象調用。
1.類方法
定義:使用裝飾器@classmethod。第一個參數必須是當前類對象,該參數名一般約定為“cls”,通過它來傳遞類的屬性和方法(不能傳實例的屬性和方法);
調用:實例對象和類對象都可以調用。
類方法有什么用???
得到類名可以實例化對象.
可以操作類的屬性.
class A:
def func(self):
print("實例方法")
@classmethod
def cls_func(cls):
print(f"cls{cls}") #cls類名
obj = cls()
print(obj) #打印A的內存地址
print("類方法")
A.cls_func()
#obj = A()
#obj.cls_func()
# 類方法: 一般就是通過類名去調用的方法,並且自動將類名地址傳給cls,
# 但是如果通過對象調用也可以,但是傳的地址還是類名地址.
例題
創建學生類,只要實例化一個對象,寫一個類方法,統計一下具體實例化多少個學生?
class Student:
count = 0
def __init__(self,name,id):
self.name = name
self.id = id
Student.addnum()
@classmethod
def addnum(cls):
cls.count = cls.count + 1
@classmethod
def getnum(cls):
return cls.count
obj1 = Student('zbb', 12343243243) #
obj1 = Student('zbb', 12343243243) #
print(Student.getnum())
2.靜態方法
使用裝飾器@staticmethod。
調用:實例對象和類對象都可以調用。
# # 靜態方法是不依賴於對象與類的,其實靜態方法就是函數.
# 保證代碼的規范性,合理的划分.后續維護性高.
#和在外面沒有區別
import time
class TimeTest(object):
area = '中國'
def __init__(self, hour, minute, second):
self.hour = hour
self.minute = minute
self.second = second
def change_time(self):
print(f'你想調整的時間: {self.hour}時{self.minute}分{self.second}秒')
@staticmethod
def showTime():
return time.strftime("%H:%M:%S", time.localtime())
def showTime():
return time.strftime("%H:%M:%S", time.localtime())
def time1():
pass
def time2():
pass
# t = TimeTest(2, 10, 10)
# # t.change_time()
# print(TimeTest.showTime())
3.屬性
property是一種特殊的屬性,訪問它時會執行一段功能(函數)然后返回值
將動態方法 偽裝 成了一個屬性,雖然在代碼級別上沒有什么提升,但是讓你看起來更合理
例一:BMI指數(bmi是計算而來的,但很明顯它聽起來像是一個屬性而非方法,如果我們將其做成一個屬性,更便於理解)
成人的BMI數值:
過輕:低於18.5
正常:18.5-23.9
過重:24-27
肥胖:28-32
非常肥胖, 高於32
體質指數(BMI)=體重(kg)÷身高^2(m)
EX:70kg÷(1.75×1.75)=22.86
class Bmi:
def __init__(self,name,height,weight):
self.name = name
self.height = height
self.weight = weight
def bmi(self):
return self.weight/self.height**2
obj = Bmi('趙嘎', 1.83, 65)
print(obj.bmi())
結果雖然實現了,但是邏輯上感覺不合理.bmi應該是類似於name,age,height,等名詞,
但是你把它當做方法使用了.
class Bmi:
def __init__(self,name,height,weight):
self.name = name
self.height = height
self.weight = weight
@property
def bmi(self):
return self.weight/self.height**2
obj = Bmi('趙嘎', 1.83, 65)
# print(obj.bmi)
# property 將執行一個函數需要函數名()變換成直接函數名.
# 將動態方法 偽裝 成了一個屬性,雖然在代碼級別上沒有什么提升,但是讓你看起來更合理.
由於新式類中具有三種訪問方式,我們可以根據他們幾個屬性的訪問特點,分別將三個方法定義為對同一個屬性:獲取、修改、刪除
class Foo:
@property
def AAA(self):
print('get的時候運行我啊')
@AAA.setter
def AAA(self,value):
print('set的時候運行我啊')
@AAA.deleter
def AAA(self):
print('delete的時候運行我啊')
#只有在屬性AAA定義property后才能定義AAA.setter,AAA.deleter
f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA
# get的時候運行我啊
# set的時候運行我啊
# delete的時候運行我啊
class Foo:
def get_AAA(self):
print('get的時候運行我啊')
def set_AAA(self,value):
print('set的時候運行我啊')
def delete_AAA(self):
print('delete的時候運行我啊')
AAA=property(get_AAA,set_AAA,delete_AAA) #內置property三個參數與get,set,delete一一對應
f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA
使用場景
class Goods(object):
def __init__(self):
# 原價
self.original_price = 100
# 折扣
self.discount = 0.8
@property
def price(self):
# 實際價格 = 原價 * 折扣
new_price = self.original_price * self.discount
return new_price
@price.setter
def price(self, value):
self.original_price = value
@price.deltter
def price(self, value):
del self.original_price
obj = Goods()
obj.price # 獲取商品價格
obj.price = 200 # 修改商品原價
del obj.price # 刪除商品原價
商品實例
商品示例
第十章 isinstace 與 issubclass
isinstance(a,b):判斷a是否是b類(或者b類的派生類)實例化的對象
class A:
pass
class B(A):
pass
obj = B()
print(isinstance(obj,B))
print(isinstance(obj,A))
isinstance
isinstace(a,b) 判斷的是 a類是否是b類 子孫類.**
issubclass(a,b): 判斷a類是否是b類(或者b的派生類)的派生類
class A:
pass
class B(A):
pass
class C(B):
pass
print(issubclass(B,A))
print(issubclass(C,A))
issubclass
思考:那么 list str tuple dict等這些類與 Iterble類 的關系是什么?
from collections import Iterable
print(isinstance([1,2,3], list)) # True
print(isinstance([1,2,3], Iterable)) # True
print(issubclass(list,Iterable)) # True
# 由上面的例子可得,這些可迭代的數據類型,list str tuple dict等 都是 Iterable的子類。
第十章 type與object聯系
type元類是獲取該對象從屬於的類,而type類比較特殊,Python原則是:一切皆對象,其實類也可以理解為'對象',而type元類又稱作構建類,
python中大多數內置的類(包括object)以及自己定義的類,都是由type元類創造的。
print(type('abc'))
print(type(True))
print(type(100))
print(type([1, 2, 3]))
print(type({'name': 'zbb'}))
print(type((1,2,3)))
print(type(object))
class A:
pass
print(isinstance(object,type))
print(isinstance(A, type))
type 與 object 的關系.
print(type(object)) object類是type類的一個實例.
object類是type類的父類.
print(issubclass(type,object))
第十一章 反射
程序可以訪問、檢測和修改它本身狀態或行為的一種能力(自省)。
python面向對象中的反射:
通過字符串的形式操作對象相關的屬性。
python中的一切事物都是對象(都可以使用反射)
四個可以實現自省的函數
下列方法適用於類和對象(一切皆對象,類本身也是一個對象)
對對象的反射
class Foo:
f="類的靜態變量"
def __init__(self,name,age):
self.name = name
self.age = age
def say_hi(self):
print(f"hello {self.name}")
#實例化對象
obj = Foo("zbb",23)
#檢測是否含有某屬性
print(hasattr(obj,"name"))
print(hasattr(obj,"say_hi"))
# 獲得屬性(可以獲得是字符串)
n = getattr(obj,"name")
# print(obj.name)
print(n)
func=getattr(obj,'say_hi')
# obj.say_hi()
func()
# print(getattr(obj,'aaaaaaaa')) #報錯
print(getattr(obj,'aaaaaaaa','不存在啊')) #報錯內容自己定義
# 設置屬性
setattr(obj,'sb',True)
setattr(obj,'show_name',lambda self:self.name+'nb')
print(obj.__dict__)
print(obj.show_name(obj))
#刪除屬性
delattr(obj,'age')
delattr(obj,'show_name')
# delattr(obj,'show_name111')#不存在,則報錯
print(obj.__dict__)
對類的反射
class A:
country = "中國"
def __init__(self,name,age):
self.name = name
self.age =age
def func(self):
print(self)
print("in A")
if hasattr(A,"country"):
print(getattr(A,"country"))
if hasattr(A,"func"):
obj = A("zbb","18")
getattr(obj,"func")()
當前模塊的反射
import sys
def s1():
print('s1')
def s2():
print('s2')
this_module = sys.modules[__name__] #本模塊這個對象
hasattr(this_module, 's1')
getattr(this_module, 's2')
在當前模塊中一次性執行4個函數
def func1():
print('in func1')
def func2():
print('in func2')
def func3():
print('in func3')
def func4():
print('in func4')
import sys
func_lst = [f'func{i}' for i in range(1,5)]
# print(func_lst)
for func in func_lst:
getattr(sys.modules[__name__],func)()
對其他模塊的反射
#一個模塊中的代碼
def test():
print('from the test')
# 另一個模塊中的代碼
import module_test as obj
#obj.test()
print(hasattr(obj,'test'))
getattr(obj,'test')()
反射的應用:
了解了反射的四個函數。那么反射到底有什么用呢?它的應用場景是什么呢?
現在讓我們打開瀏覽器,訪問一個網站,你單擊登錄就跳轉到登錄界面,你單擊注冊就跳轉到注冊界面,等等,其實你單擊的其實是一個個的鏈接,每一個鏈接都會有一個函數或者方法來處理
class User:
user_list = [('login','登錄'),('register','注冊'),('save', '存儲')]
def login(self):
print('歡迎來到登錄頁面')
def register(self):
print('歡迎來到注冊頁面')
def save(self):
print('歡迎來到存儲頁面')
while 1:
choose = input('請輸入序號: \n1: 登錄\n2: 注冊\n3: 存儲\n').strip() # 1
obj = User()
getattr(obj, obj.user_list[int(choose)-1][0])() # getattr(obj,'login')
第十二章 函數 vs 方法
1.通過打印函數(方法)名確定(了解)
def func():
pass
class A:
def func(selfs):
pass
print(func)
print(A.func)#通過類名調用的類中的實例方法叫函數
<function func at 0x00000252B19DC2F0>
<function A.func at 0x00000252B1B997B8>
obj= A()
print(obj.func)#通過對象調用的類中的實例方法叫方法.
2 通過types模塊驗證
from types import FunctionType
from types import MethodType
def func():
pass
class A:
def func(self):
pass
obj = A()
print(isinstance(func,FunctionType)) # True
print(isinstance(A.func,FunctionType)) # True
print(isinstance(obj.func,FunctionType)) # False
print(isinstance(obj.func,MethodType)) # True
總結:
python 中一切皆對象, 類在某種意義上也是一個對象,python中自己定義的類,
以及大部分內置類,都是由type元類(構建類)實例化得來的.
python 中一切皆對象, 函數在某種意義上也是一個對象,函數這個對象是從FunctionType這個類實例化出來的.
python 中一切皆對象, 方法在某種意義上也是一個對象,方法這個對象是從MethodType這個類實例化出來的.
3 靜態方法是函數
4 函數與方法的區別
那么,函數和方法除了上述的不同之處,我們還總結了一下幾點區別。
(1)函數的是顯式傳參的。
(2)函數則跟對象無關。
(3)方法中的數據則是隱形傳參.
(4)方法可以操作類內部的數據。
(5)方法跟對象是關聯的。如我們在用strip()方法是,是不是都是要通過str對象調用,比如我們有字符串s,然后s.strip()這樣調用。是的,strip()方法屬於str對象。
我們或許在日常中會口語化稱呼函數和方法時不嚴謹,但是我們心中要知道二者之間的區別。
在其他語言中,如Java中只有方法,C中只有函數,C++么,則取決於是否在類中。
第十三章 雙下方法
定義:雙下方法是特殊方法
雙下方法主要是python源碼程序員使用的,
我們在開發中盡量不要使用雙下方法,但是深入研究雙下方法,更有益於我們閱讀源碼。
調用:不同的雙下方法有不同的觸發方式,就好比盜墓時觸發的機關一樣,不知不覺就觸發了雙下方法,例如:_init
1. len
class B:
def __init__(self,name,age):
self.name = name
def __len__(self):
print(self.__dict__)
return len(self.__dict__) #必須要加
b = B("zbb",28)
print(len(b)) #len()觸發調用
2.hash
class A:
pass
obj = A()
print(hash(obj))
print(hash('fdsaf')) # hash()觸發調用
3.str重點
如果一個類中定義了__str__方法,那么在打印 對象 時,默認輸出該方法的返回值。
class A:
def __init__(self,name,age):
self.name = name
self.age =age
def __str__(self):
print(666)
return f'姓名: {self.name} 年齡: {self.age}'
a = A("zbb",18)
print(f'{a.name},{a.age}') #打印對象名觸發__str__方法
print(str(a)) #直接str轉化也可以觸發.
4.repr
如果一個類中定義了__repr__方法,那么在repr(對象) 時,默認輸出該方法的返回值。
print('我叫%s' % ('zbb'))
print('我叫%r' % ('zbb')) #保留括號里的原來的樣子
print(repr('fdsaf'))
# 我叫zbb
# 我叫'zbb'
# 'fdsaf'
class A:
def __init__(self,name,age):
self.name = name
self.age =age
def __repr__(self):
print(666)
return f'姓名: {self.name} 年齡: {self.age}'
a = A('zbb',18)
# print(a)
print(repr(a)) #print(repr(對象名)) 觸發
5.call重點
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print('__call__')
obj = Foo()
obj() # 對象名() 或者 類名()() 觸發
# Foo()()
6.eq
class A:
def __init__(self):
self.a = 1
self.b = 2
def __eq__(self,obj):
# if self.a == obj.a and self.b == obj.b:
# return True
return True
x = A()
y = A()
print(x == y) # 對象名 == 對象名 會觸發
節省內存
7.del
class A:
def __del__(self):
print(666)
obj = A()
del obj
# del 變量名 或者 類 或者 方法名時會執行
#不需要去程序員去關心,垃圾處理機制幫助處理
8.new 重點
class A:
def __init__(self):
self.x = 1
print('in init function')
def __new__(cls, *args, **kwargs):
print('in new function')
return object.__new__(A) # object 342534
a = A()
# 1. 先觸發 object的__new__方法,此方法在內存中開辟一個對象空間.
# 2. 執行__init__方法,給對象封裝屬性.
# python中的設計模式: 單例模式
# 一個類只允許實例化一個對象.
class A:
pass
obj = A()
print(obj)
obj1 = A()
print(obj1)
obj2 = A()
print(obj2) #地址都不同
手寫單例模式
方便對實例個數的控制並節約系統資源
class A:
__msg = None # 用來記錄對象信息
def __init__(self,name):
self.name = name
def __new__(cls, *args, **kwargs):
if not cls.__msg: # 當 __msg 為 None 時,證明是第一次創建對象空間
# cls.__msg = super().__new__(cls) # 調用父類的__new__方法。 記得參數為 cls(類本身)
cls.__msg = object.__new__(cls)
return cls.__msg # 將對象空間返回
a = A("zbb")
a1 = A("zxy")
print(a.name)
print(a1.name) # 兩個地址相同
9.item
# __item__系列
# __getitem__ __setitem__ __delitem__ 對對象做類似於字典的(增刪改查)觸發__item__系列
# __delattr__ del obj.屬性 就會觸發此方法
class Foo:
def __init__(self,name):
self.name=name
def __getitem__(self, item):
print(self.__dict__[item])
def __setitem__(self, key, value):
self.__dict__[key]=value
def __delitem__(self, key):
print('del obj[key]時,我執行')
self.__dict__.pop(key)
def __delattr__(self, item):
print('del obj.key時,我執行')
self.__dict__.pop(item)
f1=Foo('sb')
f1['age']=18
f1['age1']=19
del f1.age1
del f1['age']
f1['name']='alex'
print(f1.__dict__)
10.enter exit
上下文管理
# 實例化對象的第二種方式: 必須基於 __enter__ 以及 __exit__這個兩個方法.
class A:
def __init__(self, text):
self.text = text
def __enter__(self): # 開啟上下文管理器對象時觸發此方法
self.text = self.text + '您來啦' # 第一步
print(11111)
return self # 必須!!!將實例化的對象返回f1
def __exit__(self, exc_type, exc_val, exc_tb): # 執行完上下文管理器對象f1時觸發此方法
print(333) # 第三步
self.text = self.text + ',這就走啦'
with A('大爺') as f1:
print(2222)
print(f1.text) # 第二步
print(f1.text) # 第四步
11.iter
class A:
def __init__(self,name):
self.name = name
def __iter__(self):
for i in range(10):
yield i
obj = A('zbb') # obj 一個可迭代對象
# print('__iter__' in dir(obj))
for i in obj:
print(i)
class A:
def __init__(self,name):
self.name = name
def __iter__(self):
for i in range(10):
yield i
obj = A('zbb') # obj 一個可迭代對象
o = iter(obj)
print(next(o))
print(next(o))
print(next(o))