一、封裝
- 將數(shù)據(jù)和行為進(jìn)行封裝,形成類
1铺峭、類的定義
class TestClass:
# 公有類變量墓怀,可以用類名直接調(diào)用
cls_var_1 = 'pulic class variable'
# 私有類變量,只能在類內(nèi)部調(diào)用
# 定義私有變量或函數(shù)卫键,都以雙下劃線開始傀履,且不以雙下劃線結(jié)尾
__cls_var_2 = 'private class variable'
# 構(gòu)造函數(shù)(初始化函數(shù)),初始化類的時(shí)候被調(diào)用
# 以雙下劃線開始莉炉,且以雙下劃線結(jié)尾的方法钓账,都有特殊意義
def __init__(self, x, y):
# 定義實(shí)例變量(公有實(shí)例變量),只有實(shí)例化之后才能調(diào)用
self.x = x
# 定義私有實(shí)例變量絮宁,初始化之后梆暮,也只能通過內(nèi)部方法調(diào)用
self.__y = y
# 公有實(shí)例方法(第一個(gè)參數(shù)必須要有,且約定為“self”绍昂,代表類本身)
# 實(shí)例方法只是實(shí)例化之后才能被實(shí)例調(diào)用
def method_1(self, *args, **kwargs):
pass
# 私有實(shí)例方法
def __method_2(self, *args, **kwargs):
pass
# 公有類方法(第一個(gè)參數(shù)必須要有啦粹,且約定為“cls”,代表類本身)
# 公有類方法可以直接用類名調(diào)用(無需實(shí)例化)
# 類方法的的定義跟實(shí)例方法類似窘游,只需要加上classmethod的裝飾器即可
@classmethod
def method_11(cls, *args, **kwargs):
pass
# 私有類方法
@classmethod
def __method_12(cls, *args, **kwargs):
pass
# 靜態(tài)方法唠椭,使用頻率很低,不要傳遞self或cls變量
# 靜態(tài)方定義類似類方法忍饰,只需要加上staticmethod的裝飾器即可
# 法效果上:只是給方法加了個(gè)命名空間
@staticmethod
def method_111(*args, **kwargs):
pass
2泪蔫、python中的getter、setter函數(shù)
# python中的getter喘批、setter函數(shù)
class A:
def __init__(self):
self.__val = 0
# 當(dāng)實(shí)例函數(shù)的參數(shù)只有self的時(shí)候撩荣,可以加property裝飾器
# 可以將函數(shù)定義成屬性,實(shí)例化之后饶深,是需要實(shí)例名.調(diào)用即可餐曹,不用加括號
# 被property裝飾的屬性是只讀的,不能動(dòng)態(tài)修改
# 實(shí)際上敌厘,跟java中的getter函數(shù)一樣台猴,只讀且不可修改私有屬性
@property
def val(self):
if self.__val < 0:
return 0
return self.__val
# 被裝飾成setter函數(shù)的前提是,函數(shù)名必須要跟被裝飾property的函數(shù)名一樣
# setter函數(shù)接收一些參數(shù)俱两,并且在內(nèi)部做一些類型和數(shù)值判斷
@val.setter
def val(self, value):
if isinstance(value, (int, float)) and value >= 0:
self.__val = value
else:
self.__val = 0
二饱狂、繼承
1、單繼承
# 定義一個(gè)具有門牌號宪彩、開關(guān)門狀態(tài)的類“Door”
class Door:
# 初始化的時(shí)候休讳,定義了門牌號、開關(guān)門狀態(tài)
def __init__(self, number, status):
self.__number = number
self.__status = status
def open(self):
self.__status = 'is_oppened'
print("open the door")
def close(self):
self.__status = 'is_closed'
print("close the door")
# 定義一個(gè)具有鎖的門(繼承自類“Door”)
class LockableDoor(Door):
# 重寫構(gòu)造方法
def __init__(self, number, status, is_locked):
# 初始化父類的實(shí)例變量
super(LockableDoor, self).__init__(number, status)
self.__is_locked = is_locked
# 重寫父類的open方法尿孔,因?yàn)樽宇惖膐pen跟父類的不完全一樣俊柔,且有相似的地方
def open(self):
if not self.__is_locked:
# 如果門沒有上鎖筹麸,開門動(dòng)作跟Door一樣,執(zhí)行父類的open方法
super(LockableDoor, self).open()
else:
# 如果門上鎖了雏婶,執(zhí)行子類自己的方法
print('is_locked, can not open.')
# 關(guān)門的行為跟Door一樣物赶,不需要在子類中再定義,可以省略
# 定義子類中的新方法留晚,鎖門動(dòng)作是子類獨(dú)有方法酵紫,父類中沒有
def lock(self):
if self.__status == 'is_opened':
print('is openning, can not lock.')
else:
self.__is_locked = True
- 單繼承的規(guī)則
1、私有的方法和變量不可以繼承(包含類級和實(shí)例級的方法和變量)
2错维、公有的方法和變量是可繼承的(類級和實(shí)例級的)
3奖地、父類公有方法(實(shí)例級和類級)可以訪問父類的私有變量(即使子類中重新定義了同名的私有變量,調(diào)用父類的公有方法需五,訪問的依然是父類的私有變量)(父類的私有變量不會(huì)被子類覆蓋)(父類的公有變量會(huì)被子類覆蓋鹉动,包含類級的和實(shí)例級的)
上面一條的出現(xiàn),主要是私有變量改名導(dǎo)致的
2宏邮、 多繼承規(guī)則
# 定義文檔類泽示,初始化文檔的內(nèi)容
class Document:
def __init__(self, content):
self.content = content
# 定義Word類型的文檔,繼承自Document類
class Word(Document):
# Word文檔具有自己的格式化行為
def formater(self):
self.content = 'Word document: {0}'.format(self.content)
# 定義Excel類型的文檔蜜氨,繼承自Document類
class Excel(Document):
def formater(self):
self.content = 'Excel document: {0}'.format(self.content)
# 定義打印機(jī)類Printer
class Printer:
def display(self):
print('{0} printed on Printer.'.format(self.content))
# 定義顯示器類Monitor
class Monitor:
def display(self):
print('{0} printed on Monitor.'.format(self.content))
# 將多個(gè)功能單一的類組合成打印各種文檔的類(MIXIN混入模式械筛,實(shí)現(xiàn)組合)
# 1、拼湊成在打印機(jī)上打印Word文檔的類
class WordOnPrinter(Word, Printer):
pass
# 2飒炎、拼湊成在顯示器上顯示word文檔的類
class WordOnMonitor(Word, Monitor):
pass
# 3埋哟、拼湊成在打印機(jī)上打印excel文檔的類
class ExcelOnPrinter(Word, Printer):
pass
# 4、拼湊成在顯示器上顯示excel文檔的類
class ExcelOnMonitor(Word, Monitor):
pass
MIXIN實(shí)現(xiàn)了數(shù)據(jù)跟方法的分離郎汪,被組合的多個(gè)類赤赊,不能獨(dú)立運(yùn)行,必須通過組合才能發(fā)揮作用煞赢,此時(shí)抛计,不用考慮繼承順序及MRO(通過C3算法計(jì)算而來)。
- 多繼承規(guī)則;:
1照筑、不能相互繼承吹截,防止A繼承B,B繼承C凝危,C又繼承A波俄,要符合MRO規(guī)則
2、符合MIXIN的類可以隨意組合蛾默,無需考慮MRO
3懦铺、多繼承如果遇到同名函數(shù),會(huì)按照繼承列表從左至右趴生,先遇到哪個(gè)就執(zhí)行哪個(gè)阀趴;
所以昏翰,MIXIN類要寫在前面苍匆,避免MIXIN類里的方法得不到執(zhí)行刘急。
三、魔術(shù)方法
以雙下滑下開始浸踩,且以雙下劃線結(jié)尾的方法叔汁,都有特殊含義,也叫魔術(shù)方法检碗。
對象的創(chuàng)建與銷毀
1据块、' __ new__ ' 創(chuàng)建對象
2、'__ init__' 初始化對象
3折剃、'__ del__' 當(dāng)對象銷毀(垃圾回收)的時(shí)候調(diào)用可視化對象
# 定義一個(gè)類Test
class Test:
def __init__(self, name):
self.name = name
# 當(dāng)print該類的實(shí)例對象的時(shí)候另假,會(huì)調(diào)用此方法
# 如果不手動(dòng)更改返回值,默認(rèn)會(huì)返回實(shí)例對象的機(jī)器碼數(shù)值(如對戲類型怕犁,內(nèi)存地址)
# 如果定義此方法的同時(shí)边篮,還定義了__str__魔術(shù)方法,則返回__str__方法定義的字符串
def __repr__(self):
return self.name
# 當(dāng)使用str()函數(shù)查看該類的字符串時(shí)奏甫,調(diào)用此方法
def __str__(self):
return 'call__str__name is {0}'.format(self.name)
# 當(dāng)使用bytes()函數(shù)查看該類的字符串時(shí)戈轿,調(diào)用此方法
def __bytes__(self):
return 'call__str__name is {0}'.format(self.name).encode('utf-8')
用的比較多的就是__ str__可視化方法
- 比較運(yùn)算符重載
# 定義一個(gè)Person類,實(shí)現(xiàn)根據(jù)人的年齡比較大小
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# 當(dāng)該對象實(shí)現(xiàn)了以下魔術(shù)方法的時(shí)候阵子,可以實(shí)現(xiàn)根據(jù)年齡比較大小
def __lt__(self, other):
return self.age < other.age
def __le__(self, other):
return self.age <= other.age
def __eg__(self, other):
return self.age == other.age
def __ne__(self, other):
return self.age != other.age
def __ge__(self, other):
return self.age >= other.age
def __gt__(self, other):
return self.age > other.age
比較運(yùn)算符的運(yùn)算會(huì)調(diào)用相應(yīng)的比較魔術(shù)方法
- 布爾函數(shù)bool
list默認(rèn)沒有__ bool__()函數(shù)思杯,會(huì)調(diào)用__ len__()方法,判斷其長度挠进;
如果明確定義了__ bool__()函數(shù)色乾,就不會(huì)調(diào)用__ len__()方法。
- 哈希函數(shù)
__ hash__() 返回一串?dāng)?shù)字
- 可調(diào)用對象
實(shí)現(xiàn)了__ call__() 方法的類领突,就是可調(diào)用對象暖璧。
判斷一個(gè)對象是否可調(diào)用,可以使用callable()函數(shù)進(jìn)行判斷攘须。
- 使用可調(diào)用對象給類做裝飾器
# 裝飾器不僅可以裝飾方法漆撞,也可以裝飾類
# 使用類裝飾器實(shí)現(xiàn)單例模式
class Single:
# 定義一個(gè)私有類變量,存儲(chǔ)被裝飾的類的實(shí)例
__instance = None
# 初始化函數(shù)接收一個(gè)類作為參數(shù)
def __init__(self, cls):
self.cls = cls
# 定義__call__()方法于宙,被調(diào)用的時(shí)候執(zhí)行
def __call__(self, *args, **kwargs):
# 判斷是否已經(jīng)存在cls的實(shí)例
if not self.__instance:
# 如果還沒有cls類的實(shí)例出現(xiàn)浮驳,則初始化一個(gè)cls的實(shí)例
# 并將初始化的cls實(shí)例賦值給self.__instance變量
self.__instance = self.cls(*args, **kwargs)
# 返回初始化的實(shí)例
return self.__instance
@Single
class Test:
pass
test = Test() <==等價(jià)于==> Single(Test)()
當(dāng)保存一些全局性的變量,一般使用類做裝飾器捞魁。
可調(diào)用對象通常用來寫類的裝飾器至会。
- 反射
dir()就是一個(gè)標(biāo)準(zhǔn)的反射,
執(zhí)行dir()的時(shí)候谱俭,會(huì)調(diào)用對象的__ dict__屬性奉件。
調(diào)用__ dict__屬性的時(shí)候宵蛀,會(huì)以字典的形式打印出所有的實(shí)例屬性,
類屬性以及各種方法都不會(huì)顯示县貌。
getattr()函數(shù)术陶,返回類屬性和類方法,不會(huì)返回實(shí)例屬性煤痕。
例如:getattr(類名梧宫,類屬性/類方法的字符串形式)
setattr()函數(shù),動(dòng)態(tài)的給某個(gè)對象增加實(shí)例屬性
delattr()函數(shù)摆碉,動(dòng)態(tài)的刪除某個(gè)屬性
# 定義一個(gè)獲取屬性的類
class GetAttr:
def __init__(self):
self.__dic = {'x':1, 'y':2}
# 此處定義了一個(gè)字典里面不存在的屬性塘匣,在獲取的時(shí)候就不會(huì)調(diào)用__ getattr__方法
self.a = 11
# 當(dāng)定義了實(shí)例屬性x, 就不會(huì)調(diào)用__ getattr__方法
self.x = 22
# 當(dāng)獲取y屬性的時(shí)候巷帝,會(huì)先在初始化階段查找是否有y屬性
# 如果沒有忌卤,就調(diào)用__ getattr__方法繼續(xù)查找
# 如果__ getattr__方法也沒找到,才會(huì)拋出異常
def __getattr__(self, name):
print('get {0}'.format(name))
return self.__dic.get(name)
# 實(shí)例化一個(gè)GetAttr對象
g1 = GetAttr()
# 調(diào)用g1的x屬性
# 這里楞泼,在字典和初始化階段都有x屬性
# 會(huì)直接調(diào)用初始化階段的x屬性
# 不會(huì)檢查__getattr__方法
g1.x
返回 22
__ getattr__ 調(diào)用順序:先查找類本身的屬性驰徊,沒找到再調(diào)用此方法
__ getattrute__調(diào)用順序:直接調(diào)用此方法,不管找到與否都不去查找類本身的屬性
- with語句與"__ enter__" 和 "__ exit__"
當(dāng)打開一種資源现拒,進(jìn)行一系列操作之后辣垒,再關(guān)閉連接,
這個(gè)時(shí)候需要用到with語句(必入數(shù)據(jù)庫的連接操作)
# 比如印蔬,進(jìn)行文件讀取操作的時(shí)候勋桶,使用with語句
with open('./text.log') as f:
f.readline()
......
只要一個(gè)類同時(shí)實(shí)現(xiàn)了"__ enter__" 和 "__ exit__"兩個(gè)魔術(shù)方法,
就可以使用with語句侥猬。
通常情況下:將一些打開和初始化的工作在__ enter__方法里完成例驹;
結(jié)束后的清場工作,都在__ exit__方法里完成退唠。
# 定義一個(gè)資源類
class Resource:
def __init__(self):
print('init 操作')
# 定義__enter__方法鹃锈,進(jìn)入操作
def __enter__(self):
print('enter 打開資源、建立連接等操作')
# 定義__exit__方法瞧预,離開操作
def __exit__(self, *args, **kwargs):
print('exit 關(guān)閉資源屎债、關(guān)閉連接,清場操作')
# with語句塊結(jié)束的時(shí)候垢油,會(huì)自動(dòng)調(diào)用Resource類的__exit__方法進(jìn)行清場操作
with Resource() as rs:
print('rs 業(yè)務(wù)邏輯操作')
# 執(zhí)行結(jié)果如下
init 操作
enter 打開資源盆驹、建立連接等操作
rs 業(yè)務(wù)邏輯操作
exit 關(guān)閉資源、關(guān)閉連接滩愁,清場操作
可以有效的避免忘記關(guān)閉資源的后患躯喇。
四、描述器
實(shí)現(xiàn)了以下三個(gè)魔術(shù)方法的類硝枉,就是描述器
1廉丽、__ get__
2倦微、__ set__
3、__ delete__
# 定義一個(gè)Number類正压,用于描述數(shù)字類型的變量
class Number:
def __init__(self, name):
self.name = name
def __get__(self, instance, cls):
print('get')
if instance is not None:
return instance.__dict__[self.name]
return self
def __set__(self, instance, value):
print('set')
if isinstance(value, (int, float)):
instance.__dict__[self.name] = value
else:
raise TypeError('Excepted int or float type')
def __delete__(self, instance):
print('delete')
del instance.__dict__[self.name]
# 定義一個(gè)坐標(biāo)點(diǎn)類
class Point:
x = Number('x')
y = Number('y')
def __init__(self, x, y):
self.x = x
self.y =y
# 實(shí)例化一個(gè)坐標(biāo)點(diǎn)
point = Point(1, 2)
# 獲取橫坐標(biāo)
point.x # Point.x.__get__(point, Point)
描述器多用于類型檢查
以下校驗(yàn)失敗欣福,不知道問題出在哪里,待解決蔑匣。劣欢。棕诵。裁良。。校套。
# 定義一個(gè)類型定義(用于類型檢查)的描述器
class Typed:
def __init__(self, name, excepetedType):
self.name = name
self.excepetedType = excepetedType
def __get__(self, instance, cls):
print('get')
if instance is None:
return self
return instance.__dict__[self.name]
def __set__(self, instance, value):
print('set')
if isinstance(value, self.excepetedType):
instance.__dict__[self.name] = value
else:
raise TypeError('Excepted {0}, but given {1}'.format(self.excepetedType, type(value)))
def __delete__(self, instance):
del instance.__dict__[self.name]
from functools import wraps
# 定義一個(gè)類型檢查的裝飾器(帶參數(shù)的裝飾器)
def typeAssert(**kwargs):
def inner(cls):
@wraps(cls)
def wrap(**kwargs):
for k,v in kwargs.items():
# 遍歷關(guān)鍵字參數(shù)列表
# 將每一項(xiàng)參數(shù)經(jīng)過類型檢查之后价脾,以屬性的方式添加到cls
setattr(cls, k, Typed(k, v))
return cls
return wrap
return inner
@typeAssert(x=int, y=float, z=str)
class Test:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
# 以下語句沒報(bào)錯(cuò),說明出問題了笛匙,問題待解決侨把。。妹孙。秋柄。。蠢正。
test = Test(x=1, y='123', z='qwe')
描述器是一個(gè)方法傳遞協(xié)議骇笔。
五、異常
1嚣崭、異常處理
錯(cuò)誤:通常指程序運(yùn)行中不可恢復(fù)的問題笨触。
異常:通常指可以在程序運(yùn)行時(shí)恢復(fù)的錯(cuò)誤。
# 定義一個(gè)異常語句塊雹舀,基礎(chǔ)語法如下
# 將可能出現(xiàn)異常的代碼寫在try語句內(nèi)
try:
print('begin')
1 / 0
print('end')
# 下面進(jìn)行異常捕獲芦劣,使用except語句
# except語句屬于可選項(xiàng),可出現(xiàn)多次
# 按照順序依次匹配说榆,執(zhí)行最先匹配到的異常語句
# 所以虚吟,異常捕獲應(yīng)按照先子類,后父類的順序書寫except語句
except Exception as e:
print('Exception - ',e)
# as語句將捕獲到的異常的實(shí)例賦值給e變量签财,便于對異常對象的操作處理
except TypeError as e:
print('TypeError - ', e)
# 如果不需要對異常對象做任何處理串慰,也可以只寫except關(guān)鍵字,后面什么都不加
# 如果只要except關(guān)鍵字荠卷,后面沒有任何模式匹配的類型的時(shí)候模庐,會(huì)匹配所以異常類型
except ZeroDivisionError as e:
print('TypeError - ', e)
# 當(dāng)處理完異常,即將推出異常處語句塊的時(shí)候執(zhí)行finally語句
# 即使上面出現(xiàn)過return語句油宜,finally語句還是會(huì)執(zhí)行
# 通常掂碱,會(huì)在finally語句塊里面做一些資源清理或者釋放資源的操作
# 如:關(guān)閉數(shù)據(jù)庫連接怜姿,關(guān)閉正在讀寫的文件
finally:
print('finally do something...')
# 輸出結(jié)果
begin
Exception - division by zero
finally do something...
2、拋出異常
手動(dòng)拋出異常: 使用raise關(guān)鍵字
異常拋出疼燥,會(huì)拋給上層處理沧卢,如果上層沒有處理,會(huì)繼續(xù)上拋直到頂層
def fn(i):
if i < 0:
raise Exception('i<0')
BaseException
- Exception
- GeneratorExit
- KeyboardInterrupt
- SystemExit
3醉者、自定義異常
定義一個(gè)類但狭,繼承Exception即可
編寫庫的時(shí)候,通常會(huì)自定義一些異常撬即,方便調(diào)用者排查故障
六立磁、模塊化
Python中,模塊剥槐、包和庫的概念沒有清晰的邊界
一個(gè)文件就是一個(gè)模塊唱歧,模塊名就是文件名
一個(gè)目錄,包含了__ init__.py就是一個(gè)包
通常粒竖,當(dāng)一個(gè)或者若干個(gè)包颅崩,包含了一個(gè)setup.py就認(rèn)為是一個(gè)庫
導(dǎo)入一個(gè)模塊,實(shí)際上就是執(zhí)行該模塊蕊苗,所以不要在模塊里編寫全局性的操作語句沿后。
- 相對導(dǎo)入和絕對導(dǎo)入
同一包內(nèi)的模塊可以使用相對導(dǎo)入,也可以使用絕對導(dǎo)入朽砰。
1尖滚、相對引用:
引用同一級目錄下的模塊,使用.加模塊名
引用載上一級的模塊锅移,使用..加模塊名(再上一級使用...加模塊名)
2熔掺、絕對引用:
from 完整的模塊路徑名 import 模塊下的方法
如果是同一目錄下的引用,一般使用相對引用非剃,當(dāng)頂層的包名修改的時(shí)候置逻,引用不受影響。當(dāng)相對引用的層次太深的時(shí)候备绽,會(huì)導(dǎo)致引用者搞不清引用的是哪個(gè)包券坞。
- 循環(huán)導(dǎo)入
應(yīng)避免循環(huán)引用(會(huì)報(bào)錯(cuò))
- 發(fā)布自己的庫
# 在要發(fā)布的包內(nèi),創(chuàng)建一個(gè)setup.py的文件即可
# 文件內(nèi)容肺素,大致如下(引入標(biāo)準(zhǔn)庫的方式)
from distutils.core import setup
# setup函數(shù)接收一個(gè)可變關(guān)鍵字參數(shù)
# 以字典的形式傳遞參數(shù)
setup(**{
'name': '庫名'恨锚,
'version': '1.0.0',
# 目錄名(不會(huì)級聯(lián)子目錄,需要單獨(dú)指定)
'packages': ['包列表', '包列表.子目錄'] ,
# 安裝該包的時(shí)候倍靡,依賴的庫或者包列表(可選項(xiàng))
'requires': ['依賴的庫名']
})
# 也可用關(guān)鍵字參數(shù)的形式傳遞參數(shù)
setup(
name = '庫名'猴伶,
version = '1.0.0',
packages = ['要發(fā)布的包列表']
)
# 創(chuàng)建好setup.py之后,在setup.py所在目錄執(zhí)行build命名
$ python setup.py build
# 引入第三方增強(qiáng)庫的方式編寫setup.py文件(通常使用這種方式)
from setuptools import setup, find_packages
setup(**{
'name': '庫名',
'version': '1.0.0',
# find_packages函數(shù)會(huì)自動(dòng)查找目錄
'packages': find_packages(),
'install_requires': ['依賴的庫名==(>=)版本號']
})
發(fā)布出來之后他挎,會(huì)生成一個(gè)目錄文件筝尾。
安裝的時(shí)候,只需要進(jìn)入該目錄办桨,執(zhí)行pip命令即可:
$ pip install .
也可以筹淫,使用python命令安裝:
$ python setup.py install
如果放在而來GitHub上,也可以使用pip安裝:
$ pip install git+URL
- pip常用命令
1呢撞、freeze:列出當(dāng)前項(xiàng)目正在使用的庫和包(包含庫的具體版本)
# 將當(dāng)前項(xiàng)目的依賴包到處到requirements.txt文件中
$ pip freeze > requirements.txt
2损姜、install:安裝命令
# 從該文件讀取并安裝
$ pip install -r requirements.txt