三大編程範式(面向過程編程、函數式編程、面向對象編程)python
編程範式就是編程的方法論,表示一種編程風格,不一樣風格沒有高低之分編程
編程進化論:非結構化程序不斷地結構化(函數區分功能,面向對象把功能和數據整合到一塊兒)後端
面向對象設計例子(不必定要用class來寫,def也能夠寫):app
def dog(name, gender, type):
def jiao(dog):
print("一條狗[%s], barking" %dog["name"])
def play(dog):
print("一條[%s]playing" %dog["type"])
def init(name, gender, type):
dog1 = {"name": name,
"gender": gender,
"type": type,
"jiao": jiao,
"play": play,
}
return dog1
return init(name, gender, type)
d1 = dog('aaa', 'male', 'za')
d2 = dog('bbb', 'female', 'jb')
print(d1)
print(d2)
d1['jiao'](d1)
d2['play'](d2)
類:把一類事物的相同特徵和動做(數據屬性和函數屬性)整合到一塊兒就是類,類是一個抽象的概念(只是一個模板、總結概括)函數式編程
對象:就是基於類而建立的一個具體的事物(具體存在的),也是特徵和動做整合到一塊兒函數
面向對象設計:把數據屬性和函數屬性整合到一塊兒造成類,就是面向對象設計,經過類實例化出對象,就叫實例化的過程工具
面向對象編程:固定格式,用class定義類的方式去寫面向對象設計的程序this
在python中聲明函數和聲明類很類似spa
聲明類名時,規範是類名首字母要大寫設計
類名加()表示實例化出一個對象(對象=實例),類的返回值就是一個具體的實例,本質也是一個return,函數加()表示運行一段邏輯
class Chinese:
'this is a class of Chinese'
pass
print(Chinese)#結果是<class '__main__.Chinese'>,右鍵執行文件時,文件名變爲__main__
p1 = Chinese()#實例化
print(p1)
經典類與新式類:python2中區分經典類和新式類
經典類特色:類名後直接是冒號
新式類特色:類名後是(object)
python3中統一都是新式類
類是用來描述一類事物,類的對象指的是這一類事物中的一個個體
是事物就要有屬性,屬性分爲:
1.數據屬性:就是變量
2.函數屬性:就是函數,在面向對象裏一般稱爲方法
類和對象均用點來訪問本身的屬性
類生產對象的過程叫作實例化,類實例化的結果就是一個對象,或者叫作一個實例 (對象=實例)
class Chinese:
'這是一箇中國人的類'
party = 'cp'
def spit():
print('正在spit')
def cut_in_line(self):
print('走到了前面')
print(Chinese.party)#類和對象均用點來訪問本身的屬性,本質上是到本身的屬性字典裏找東西
Chinese.spit()
print(dir(Chinese))#查看函數屬性,輸出是一個列表,裏面是屬性名
print(Chinese.__dict__)#查看類的屬性字典,key爲屬性名,value爲屬性值
print(Chinese.__dict__['party'])
Chinese.__dict__['spit']()
類的其餘屬性:
class Chinese:
'這是一箇中國人的類'
party = 'cp'
def spit():
print('正在spit')
def cut_in_line(self):
print('走到了前面')
print(Chinese.__name__)#顯示類名
print(Chinese.__doc__)#顯示類的文檔
print(Chinese.__base__)#python中全部的類都有一個共同的祖先:object
print(Chinese.__bases__)#以元組的形式顯示類的祖先
print(Chinese.__module__)#顯示類所在模塊
print(Chinese.__class__)#實例對應的類(僅新式類中)
class Chinese:
'這是一箇中國人的類'
party = 'cp'
def __init__(name, age, gender): #類會自動檢索到這個初始化函數,會自動將類接受的參數傳給初始化參數,
初始化函數邏輯加載一遍後會自動return 一個字典,不須要用return __init__()來接收它return的值
dic = {'name': name, 'age': age, 'gender': gender}
return dic
def spit():
print('正在spit')
def cut_in_line(self):
print('走到了前面')
p1 = Chinese('cyx', 13, 'male')#實例化,參數會自動傳給__init__函數
class Chinese:
'這是一箇中國人的類'
party = 'cp'
def __init__(self, name, age, gender):#類當中必須有一個初始化的函數來定製每個對象的屬性,函數必須有一個self參數,其次再寫數據屬性
print('start running')
self.mingzi = name #self是實例自己,就是p1,self.mingzi表明給self賦予了一個mingzi屬性
self.nianji = age #p1.nianji = age
self.xingbie = gender
print('end running')
def spit():
print('正在spit')
def cut_in_line(self):
print('%s 走到了前面' %self.mingzi)
p1 = Chinese('cyx', 13, 'male')#實例化,參數會自動傳給__init__函數,實例化本質上就是調用了init函數的運行,p1傳給了self作參數
print(p1.__dict__)#輸出p1的字典形式,實例只有數據屬性,沒有函數屬性,由於實例是執行__init__方法生成的一個字典,裏面並不包含函數屬性
print(p1.__dict__['mingzi'])#同下一行代碼
print(p1.xingbie)
print(p1.party)#實例如今__init__的做用域裏找key,沒找到,再到外層Chinese中找到party,若是類中找不到就會報錯,因此實例能夠調到類的數據屬性,跟函數的做用域同樣
Chinese.cut_in_line(p1)
# p1.spit()#class會默認將p1傳給 spit(),spit()不須要參數,因此會報錯
p1.cut_in_line()#class會默認將p1傳給cut_in_line()
實例只有數據屬性,函數屬性找類要,這麼作的好處是:實例經過引用的方式去調類的屬性,這樣能夠省內存
class Chinese:
'這是一箇中國人的類'
party = 'cp'
def __init__(self, name, age, gender):
self.mingzi = name
self.nianji = age
self.xingbie = gender
def spit(self):
print('%s正在spit' %self.mingzi)
def cut_in_line(self):
print('%s 走到了前面' %self.mingzi)
def eat_food(self, food):
print('%s正在吃%s' %(self.mingzi, food))
p1 = Chinese('cyx', 13, 'male')
p1.spit()
p1.eat_food('banana')
p2 = Chinese('jik', 12, 'male')
p2.eat_food('apple')
print(dir(p2))#實例也有dir方法
凡是類調用的方法,默認都有一個參數self,放在函數的第一個位置,這是函數獨有的語法結構要求的,這樣在調用每一個函數時,類會自動把實例傳到第一個位置
class Chinese:
'這是一箇中國人的類'
party = 'cp'
def __init__(self, name, age, gender):
self.mingzi = name
self.nianji = age
self.xingbie = gender
def spit(self):
print('%s正在spit' %self.mingzi)
def cut_in_line(self):
print('%s 走到了前面' %self.mingzi)
def eat_food(self, food):
print('%s正在吃%s' %(self.mingzi, food))
#查看類屬性
print(Chinese.party)
#修改類屬性
#改數據屬性
Chinese.party = 'CP'
print(Chinese.party)
p1 = Chinese('yjj', 23, 'girl')
print(p1.party)
#改函數屬性
def test(self):
print('test')
Chinese.play_ball = test
p1.play_ball()#類的函數屬性修改會立馬體現給實例,由於實例是經過調用類的函數
#增長類屬性
#加數據屬性
Chinese.country = 'China'
print(Chinese.country)
print(p1.country)
#加函數屬性
def play_ball(self, ball):
print('%s在打%s' %(self.mingzi, ball))
Chinese.play = play_ball
p1.play('football')
#刪除類屬性
del Chinese.party
del Chinese.country
print(Chinese.__dict__)
class Chinese:
'這是一箇中國人的類'
party = 'cp'
def __init__(self, name, age, gender):
self.mingzi = name
self.nianji = age
self.xingbie = gender
def eat_food(self, food):
print('%s正在吃%s' % (self.mingzi, food))
#生成實例
p1 = Chinese('ypp', '18', 'boy')
#查看實例屬性字典
print(p1.__dict__)
#查看實例屬性
print(p1.mingzi)
print(p1.eat_food)
#增長
p1.hobby = 'reading'
print(p1.__dict__)
print(p1.hobby)
def test(self):
print('實例的函數屬性')
p1.test = test
print(p1.__dict__)
print(p1.test)
# p1.test()#報錯,這裏p1調用的是實例的類,class只有在實例調用類的方法時,纔會自動傳self
p1.test(p1)#上一行代碼修改爲此行便可運行
# #不要修改底層的屬性字典,雖然能夠實現
# p1.__dict__['gh'] = 'jk'
# print(p1.__dict__)
# print(p1.gh)
#修改
p1.nianji = 45
print(p1.__dict__)
print(p1.nianji)
#刪除
del p1.nianji
print(p1.__dict__)
例1:
class Chinese:
country='China'
def __init__(self, name):
self.name = name
def play_ball(self, ball):
print('%s 正在打 %s' %(self.name, ball))
p1=Chinese('alex')
print(p1.country)#訪問類的屬性
p1.country = 'Japan'#在p1字典裏新增,跟類字典沒有關係
print(Chinese.country)#China
print(p1.country)#Japan
例2:
country = 'China'
class Chinese:
def __init__(self, name):
self.name = name
def play_ball(self, ball):
print('%s 正在打 %s' %(self.name, ball))
p1=Chinese('alex')
print(p1.country)#報錯,p1只在類裏面找,找不到不會去外面找
例3:init只特殊在self,不能return值,其他跟函數沒有區別
country = 'China'
class Chinese:
def __init__(self):
name = input('請輸入用戶名:')
self.name = name
def play_ball(self, ball):
print('%s 正在打 %s' %(self.name, ball))
p1=Chinese()
print(p1.__dict__)
此例屬於輸入和函數耦合到一塊兒,不符合規範,修改以下
country = 'China'
class Chinese:
def __init__(self, name):
self.name = name
def play_ball(self, ball):
print('%s 正在打 %s' %(self.name, ball))
name = input('>>>:')
p1=Chinese(name)
print(p1.__dict__)
例4:
country = 'China'
class Chinese:
def __init__(self, name):
self.name = name
print(country)#沒加點代表其既不是類的屬性也不是實例的屬性,只是一個普通變量,能夠到類外面找到全局變量
def play_ball(self, ball):
print('%s 正在打 %s' %(self.name, ball))
p1=Chinese('cyx')
例5:
country = 'China2'
class Chinese:
country = 'China1'#只有類或實例加.能夠調用到,country = 'China1'存在於類的字典裏,普通變量country不會跑到類或實例的字典裏去找
def __init__(self, name):
self.name = name
print(country)#調不到類的country
def play_ball(self, ball):
print('%s 正在打 %s' %(self.name, ball))
p1=Chinese('cyx')
例6:
class Chinese:
country = 'China'#只有類或實例加.能夠調用到,country = 'China1'存在於類的字典裏,普通變量country不會跑到類或實例的字典裏去找
l = ['a', 'b']
def __init__(self, name):
self.name = name
def play_ball(self, ball):
print('%s 正在打 %s' %(self.name, ball))
p1=Chinese('cyx')
print(p1.l)
# p1.l = [1, 2, 3]#給p1新增一個屬性
# print(p1.__dict__)
# print(Chinese.l)
p1.l.append('c')#沒有給p1新定義屬性,l.append操做的是類的屬性
print(p1.__dict__)
print(Chinese.l)
class Room:
def __init__(self, name, owner, width, length, height):
self.name = name
self.owner = owner
self.width = width
self.length = length
self.height = height
@property #能夠封裝邏輯,本身定製一段邏輯,讓用戶調用的時候徹底感知不到後端執行的是什麼樣的邏輯,就像是在調用普通的數據屬性同樣
def cal_area(self):
return self.width * self.length
r1 = Room('bedroom', 'tom', 100, 100, 10000)
r2 = Room('baseroom', 'jerry', 10, 10, 100)
print(r1.cal_area)
print(r2.cal_area)#讓函數屬性看起來像數據屬性,調用方式不須要加()
用處:跟實例沒有任何關係,只是類級別的操做的時候會用
class Room:
tag = 1
def __init__(self, name, owner, width, length, height):
self.name = name
self.owner = owner
self.width = width
self.length = length
self.height = height
@property
def cal_area(self):
return self.width * self.length
def test(self):
print('from test', self.name)
@classmethod#專門給類用的
def tell_info(cls, x):
print(cls)
print(cls.tag, x)
Room.tell_info(10)#會自動傳入第一個參數Room
class Room:
tag = 1
def __init__(self, name, owner, width, length, height):
self.name = name
self.owner = owner
self.width = width
self.length = length
self.height = height
@property
def cal_area(self):
return self.width * self.length
def test(self):
print('from test', self.name)
@classmethod#專門給類用的
def tell_info(cls, x):
print(cls)
print(cls.tag, x)
@staticmethod
def play_ball(a, b, c):
print('%s %s %s正在踢球' %(a, b, c))#類和實例調用都沒有問題
def test2(x, y):
print(x, y)#毫無心義,這不是靜態方法,用類調用沒問題,用實例調用會出問題 ,由於實例會把本身傳入第一個參數,無人接收
Room.play_ball('tom', 'jerry', 'jack')
r1 = Room('bedroom', 'tom', 100, 100, 10000)
r1.play_ball('tom', 'jerry', 'jack')
Room.test2(1, 2)
r1.test2(1, 2)#會報錯
class School:
def __init__(self, name, addr):
self.name = name
self.addr = addr
def enroll_stu(self):
print('%s 正在招生' %self.name)
class Course:
def __init__(self, name, price, period, school):
self.name = name
self.price = price
self.period = period
self.school = school
s1=School('xdf','北京')
s2=School('xdf','南京')
s3=School('xdf','天津')
msg = '''
1新東方 北京校區
2新東方 南京校區
3新東方 天津校區
'''
while True:
print(msg)
menu = {
'1': s1,
'2': s2,
'3': s3,
}
choice = input('請選擇學校:')
sch_obj = menu[choice]
name_choice = input('課程名:')
price_choice = input('課程費用:')
period_choice = input('課程週期:')
new_course = Course(name_choice, price_choice, period_choice, sch_obj)
print("課程%s屬於%s學校" %(new_course.name, new_course.school.name))
繼承分單繼承(括號裏有一個父類)和多繼承(括號裏有多個父類)
class SubClass(ParentClass1, ParentClass2):
子類繼承了父類的全部屬性,子類自定義的屬性若是跟父類重名,等於子類在本身的字典裏新建了一個屬性,找的時候如今本身這裏找
class Dad:
'這個是父類'
money = 10
def __init__(self, name):
print('father')
self.name = name
def hit_son(self):
print('%s正在打兒子' %self.name)
class Son(Dad):
money = 1000000
s1 = Son('cyx')
print(s1.money)
print(Dad.money)
接口繼承:表明父類當中規定好了子類必須實現什麼方法,可是父類裏不去實現,子類只要繼承了父類,子類必須在本身的類中去具體的實現父類規定的方法
接口:一個方法,一個具體的函數
歸一化設計:使得高層的外部使用者能夠不加區分的處理全部接口兼容的對象集合
import abc
class All_file(metaclass=abc.ABCMeta):#父類規定全部子類必須實現的方法,接口類的方法不用實現內部邏輯,只是來規範子類,沒有必要實例化
@abc.abstractmethod
def read(self):#接口
pass
@abc.abstractmethod
def write(self):
pass
class Disk(All_file):
def read(self):
print('disk read')
def write(self):
print('disk write')
class Cdrom(All_file):
def read(self):
print('cdrom read')
def write(self):
print('cdrom write')
class Mem(All_file):
def read(self):
print('mem read')
m1 = Mem()#會報錯,由於父類當中規定好了子類必須實現什麼方法,可是父類裏不去實現,子類只要繼承了父類,子類必須在本身的類中去具體的實現父類規定的方法
繼承順序
class A:
def test(self):
print('A')
class B(A):
# def test(self):
# print('B')
pass
class C(A):
# def test(self):
# print('C')
pass
class D(B):
# def test(self):
# print('D')
pass
class E(C):
# def test(self):
# print('E')
pass
class F(D, E):
# def test(self):
# print('F')
pass
f1 = F()
f1.test() #F D B E C A 新式類的繼承順序
F D B A E C 經典類的繼承順序
經典類深度優先,新式類廣度優先
print(F.__mro__)
#(<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
python2中的經典類和新式類
子類中調用父類的方法:
方法1:可擴展性差,一旦基類名修改 ,子類裏都得改
class Vehicle:
Country='China'
def __init__(self,name,speed,load,power):
self.name=name
self.speed=speed
self.load=load
self.power=power
def run(self):
print('開動啦')
print('開動啦')
class Subway(Vehicle):
def __init__(self,name,speed,load,power,line):
Vehicle.__init__(self,name,speed,load,power)
self.line=line
def show_info(self):
print(self.name,self.speed,self.load,self.power,self.line)
def run(self):
Vehicle.run(self)
print('%s %s 線,開動啦' %(self.name,self.line))
line13=Subway('北京地鐵','10km/s',1000000000,'電',13)
line13.show_info()
line13.run()
方法2:不用寫父類名,不用傳self參數
class Vehicle:
Country='China'
def __init__(self,name,speed,load,power):
self.name=name
self.speed=speed
self.load=load
self.power=power
def run(self):
print('開動啦')
print('開動啦')
class Subway(Vehicle):
def __init__(self,name,speed,load,power,line):
# Vehicle.__init__(self,name,speed,load,power)
super().__init__(name, speed, load, power)#=super(Subway, self).__init__(name, speed, load, power)#不用加self
self.line=line
def show_info(self):
print(self.name,self.speed,self.load,self.power,self.line)
def run(self):
# Vehicle.run(self)
super().run()#不用加self
print('%s %s 線,開動啦' %(self.name,self.line))
line13=Subway('北京地鐵','10km/s',1000000000,'電',13)
line13.show_info()
line13.run()
由不一樣的類實例化獲得的對象,調用同一個方法,執行的邏輯不一樣
class H2O:
def __init__(self, name, temperature):
self.name = name
self.temperature = temperature
def turn_ice(self):
if self.temperature < 0:
print('【%s】 變成冰' %self.name)
elif self.temperature > 0 and self.temperature < 100:
print('【%s】 變成水')
elif self.temperature > 100:
print('【%s】 變成水蒸氣')
class Water(H2O):
pass
class Ice(H2O):
pass
class Steam(H2O):
pass
w1 = Water('水', 5)
i1 = Ice('冰', -6)
s1 = Steam('蒸汽', 134)
def func(obj):
obj.turn_ice()
func(w1)
func(i1)
func(s1)
# w1.turn_ice()
# i1.turn_ice()
# s1.turn_ice()
第一個層面的封裝:類自己就是在封裝數據屬性和函數屬性
第二個層面的封裝:類中定義私有的,只在類內部使用,外部沒法訪問
約定1:任何以單下劃線開頭的名字都應該是內部的、私有的
真要調用也能夠調用,只是不符合約定
約定2:雙下劃綫開頭的名字
直接__名字調用不了,要用_類名__名字調用才能夠
class H2O:
__sdf = "jkl"
def __init__(self, name, temperature):
self.name = name
self.temperature = temperature
def turn_ice(self):
if self.temperature < 0:
print('【%s】 變成冰' %self.name)
elif self.temperature > 0 and self.temperature < 100:
print('【%s】 變成水')
elif self.temperature > 100:
print('【%s】 變成水蒸氣')
class Water(H2O):
pass
class Ice(H2O):
pass
class Steam(H2O):
pass
w1 = Water('水', 5)
# print(w1._sdf)
# print(w1.__sdf)
print(w1._H2O__sdf)
第三個層面的封裝:真正意義的封裝,明確區份內外,內部的實現邏輯,外部沒法知曉,而且爲封裝到內部的邏輯提供一個訪問接口給外部使用
class Room:
def __init__(self, name, owner, width, length, height):
self.name = name
self.owner = owner
self.__width = width
self.__length = length
self.__height = height
def tell_area(self):
return self.__width * self.__length
r1 = Room('restroom', 'cyx', 100, 100, 45)
print(r1.tell_area())
指程序能夠訪問、檢測和修改它自己狀態或行爲的一種能力。
四個能夠實現自省的函數:hasattr getattr setattr delattr
class DishonestMiddleman:
feature = 'ugly'
def __init__(self, name, addr):
self.name = name
self.addr = addr
def sell_house(self):
print('%s正在賣房子' %self.name)
def rent_house(self):
print('%s正在租房子' %self.name)
#hasattr檢測d1可否調用到''裏的屬性
d1 = DishonestMiddleman('dabeitou', 'toilet')
print(hasattr(d1, 'name'))
print(hasattr(d1, 'sell_house'))
print(hasattr(d1, 'sell_hrtyuuouse'))#返回false
#getattr(x, 'y')=x.y
print(getattr(d1, 'name'))#=d1.name
print(getattr(d1, 'sell_house'))
func = getattr(d1, 'sell_house')
func()
# print(getattr(d1, 'selsdfl_house'))#沒有則報錯
print(getattr(d1, 'sell_houfghhse', 'no such attribute'))#寫了默認參數找不到也不後會報錯,會返回默認參數
setattr(d1, 'sb', True)#=d1.sb = Truekey值不存在時,新增屬性
setattr(d1, 'name', 'SB')#key值存在時,修改屬性
setattr(d1, 'func', lambda x:x+1)
setattr(d1, 'func1', lambda self:self.name + 'sb')
print(d1.__dict__)
print(d1.func(10))
print(d1.func1(d1))
#delattr(x, 'y') = del x.y
delattr(d1, 'name')
print(d1.__dict__)
動態導入模塊:__import__('文件名 ')
from m1 import t#m1/t
#如何以字符串形式導入模塊
module = __import__('m1.t')#導入時會將t.py運行一遍,無論套多少層,最後返回的是最頂層的模塊
print(module)#__import__經過字符串的形式導入模塊,導入是最頂級的模塊
module.t.test1()
# from m1.t import *
#
# test1()
# test2()#若是在模塊屬性前加_,就不能在import*時被導入,可是Python沒有真正意義上的限制,解決方法以下
from m1.t import test1, _test2
test1()
_test2()
import importlib
m = importlib.import_module('m1.t')
print(m)#獲得的結果是m1.t模塊,不一樣於__import__
class DishonestMiddleman:
feature = 'ugly'
def __init__(self, name, addr):
self.name = name
self.addr = addr
def sell_house(self):
print('%s正在賣房子' %self.name)
def rent_house(self):
print('%s正在租房子' %self.name)
print(hasattr(DishonestMiddleman, 'feature'))#類本質上也是對象
class Foo:
x = 1
def __init__(self, y):
self.y = y
def __getattr__(self, item):
print('執行__getattr__')
#調用對象不存在的屬性時,__getattr__纔會觸發運行
f = Foo(10)
f.ggggg
class Foo:
x = 1
def __init__(self, y):
self.y = y
def __delattr__(self, item):
print('執行__delattr__')
f = Foo(10)
#刪除時會觸發__delattr__
del f.y
del f.x
class Foo:
x = 1
def __init__(self, y):
self.y = y
def __setattr__(self, key, value):
print('執行__setattr__')
# self.key = value會進入無限遞歸,由於一執行就會觸發__setattr__
self.__dict__[key] = value
f = Foo(10)
f.z = 3
print(f.__dict__)
class Foo:
pass
f1 = Foo()
# print(f1.x)#只有屬性不存在時,會自動觸發__getattr__,直接報錯,如有自定義__getattr__,則使用自定義的__getattr__
# del f1.x#刪除屬性時會觸發__delattr__
f1.x = 10#設置屬性時會觸發__setattr__
class Foo:
def __init__(self, name):
self.name = name
def __getattr__(self, item):
print('你找的屬性【%s】不存在' %item)
f1 = Foo('cyx')
print(f1.age)
需求:添加的屬性必須是字符串類型
class Foo:
def __init__(self, name):
self.name = name
def __getattr__(self, item):
print('你找的屬性【%s】不存在' %item)
def __setattr__(self, key, value):
print('執行setattr', key, value)
if type(value) is str:
print('開始設置')
# self.k = v#會觸發__setattr__,繼而進入死循環
self.__dict__[key] = value.upper()#能夠定製設置屬性的過程
else:
print('必須是字符串類型')
f1 = Foo('cyx')#觸發self.name = name,進而觸發__setatr__
f1.age = 18#觸發__setattr__
f1.gender = 'male'
print(f1.__dict__)
需求:不容許刪除屬性
class Foo:
def __init__(self, name):
self.name = name
def __getattr__(self, item):
print('你找的屬性【%s】不存在' %item)
def __setattr__(self, key, value):
print('執行setattr', key, value)
if type(value) is str:
print('開始設置')
# self.k = v#會觸發__setattr__,繼而進入死循環
self.__dict__[key] = value.upper()#能夠定製設置屬性的過程
else:
print('必須是字符串類型')
def __delattr__(self, item):
print('不容許刪除屬性【%s】' %item)
# # del self.item#觸發__delattr__,繼而進入死循環
# self.__dict__.pop(item)
f1 = Foo('cyx')#觸發self.name = name,進而觸發__setatr__
print(f1.__dict__)
del f1.name
print(f1.__dict__)
class List(list):
def append(self, p_object):
if type(p_object) is str:
# self.append(p_object)會陷入無限遞歸
list.append(self, p_object)#推薦用super().append(p_object)
else:
print('必須是字符串類型')
def show_middle(self):
middle_index = int(len(self)/2)
return self[middle_index]
l1 = List('helloworld')
print(l1, type(l1))
print(l1.show_middle())
l1.append('cyx')
print(l1)
l1.append(1765)
isinstance 和issubclass
class Foo:
pass
class Bar(Foo):
pass
f1 = Foo()
print(isinstance(f1, Foo))
print(issubclass(Bar, Foo))
__getattribute__
class Foo:
def __init__(self, x):
self.x = x
def __getattr__(self, item):
print('執行__getattr__')
def __getattribute__(self, item):#無論屬性找不找的到,都會觸發getattribute
print('執行__getattribute__')
raise AttributeError('拋出異常了')#__getattribute__拋出AttributeError異常時纔會觸發__getattr_運行
f1 = Foo(10)
f1.x
f1.xxxxxx
__setitem__ __getitem__ __delitem__:[]的方式操做纔會觸發,.的方式操做不會觸發
class Foo:
def __getitem__(self, item):
print('getitem')
def __setitem__(self, key, value):
print('setitem')
def __delitem__(self, key):
print('delitem')
f1 = Foo()
print(f1.__dict__)
f1['name'] = 'cyx'
f1['age'] = 18
print(f1.__dict__)#由於setitem裏沒有定製屬性的邏輯,因此輸出是{}
class Foo:
def __getitem__(self, item):
print('getitem')
def __setitem__(self, key, value):
print('setitem')
self.__dict__[key] = value
def __delitem__(self, key):
print('delitem')
self.__dict__.pop(key)
f1 = Foo()
print(f1.__dict__)
f1['name'] = 'cyx'
f1['age'] = 18
print(f1.__dict__)#由於setitem有定製屬性的邏輯,因此輸出是{'name': 'cyx', 'age': 18}
del f1.name#會觸發__delattr__,不會觸發__delitem__
print(f1.__dict__)
print(f1.age)#會觸發__getattr__,不會觸發__getitem__
class Foo:
def __getitem__(self, item):
print('getitem')
return self.__dict__[item]
def __setitem__(self, key, value):
print('setitem')
self.__dict__[key] = value
def __delitem__(self, key):
print('delitem')
self.__dict__.pop(key)
f1 = Foo()
print(f1.__dict__)
f1['name'] = 'cyx'
f1['age'] = 18
print(f1.__dict__)#由於setitem有定製屬性的邏輯,因此輸出是{'name': 'cyx', 'age': 18}
del f1['name']
print(f1.__dict__)
print(f1['age'])
改變對象的字符串顯示:__str__ __repr__
class Foo:
pass
f1 = Foo()
print(f1)#print觸發系統內的__str__(f1)→f1.__str__(),輸出<__main__.Foo object at 0x0000000001E7A9E8>
class Foo:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return '名字是%s 年齡是%s' %(self.name, self.age)
f1 = Foo('cyx', 18)#再也不輸出系統默認的格式,而是輸出類中定製的格式名字是cyx 年齡是18
print(f1)
x = str(f1)
print(x)#至關於print(f1)
y = f1.__str__()
print(y)#至關於print(f1)
class Foo:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return '這是__str__'#返回值必須是字符串
def __repr__(self):
return '名字是%s 年齡是%s' %(self.name, self.age)#返回值必須是字符串
f1 = Foo('cyx', 18)
print(f1)#print找的順序str(f1) f1.__str__() repr(f1) f1.__repr__()
總結:str函數或者print函數→obj.__str__()
repr或者交互式解釋器→obj.__repr__()
若是__str__沒有被定義,那麼就會使用__repr__
注意:這兩個方法的返回值必須是字符串 ,不然拋出異常
__format__
x = '{0}{0}{0}'.format('dog')
print(x)#dogdogdog
class Date:
def __init__(self, year, mon, day):
self.year = year
self.mon = mon
self.day = day
d1 = Date(2018, 5, 20)
x = '{0.year}{0.mon}{0.day}'.format(d1)
y = '{0.year}:{0.mon}:{0.day}'.format(d1)
z = '{0.mon}-{0.day}-{0.year}'.format(d1)
print(x)
print(y)
print(z)
format_dic = {
'ymd': '{0.year}{0.mon}{0.day}',
'y:m:d': '{0.year}:{0.mon}:{0.day}',
'm-d-y': '{0.mon}:{0.day}:{0.year}'
}
class Date:
def __init__(self, year, mon, day):
self.year = year
self.mon = mon
self.day = day
def __format__(self, format_spec):
if not format_spec or format_spec not in format_dic:
format_spec = 'ymd'
fm = format_dic[format_spec]
return fm.format(self)
d1 = Date(2018, 5, 20)
print(d1.__format__('ymd'))
print(d1.__format__('y:m:d'))
print(d1.__format__('m-d-y'))
print(d1.__format__(''))
print(d1.__format__('hkhg'))
print(format(d1, ''))
print(format(d1))
__slots:定義在類中的類變量,用來省內存,實例只能定義slots提供的屬性
class Foo:
__slots__ = 'name'#{''name': None},這個類產生的實例再也不具備__dict__屬性,限制了實例的屬性建立
f1 = Foo()
f1.name = 'cyx'
print(f1.name)
# print(f1.__dict__)
# f1.age = 18本質是觸發__setattr__,進一步觸發f1.__dict__['age'] = 18
print(Foo.__slots__)
print(f1.__slots__)
__doc__:查看描述信息
class Foo:
'描述信息'
pass
class Bar(Foo):
pass
print(Foo.__dict__)
print(Bar.__dict__)
print(Foo.__doc__)
print(Bar.__doc__)#__doc__屬性沒法被繼承
__module__和__class__查看實例來自哪一個模塊和來自哪一個類
析構方法__del__
class Foo:
def __init__(self, name):
self.name = name
def __del__(self):
print('執行__del__')
f1 = Foo('cyx')
del f1
print('---->')
class Foo:
def __init__(self, name):
self.name = name
def __del__(self):
print('執行__del__')
f1 = Foo('cyx')
print('---->')#文件執行完後,內存釋放,會觸發__del__
class Foo:
def __init__(self, name):
self.name = name
def __del__(self):
print('執行__del__')
f1 = Foo('cyx')
del f1.name#del在實例被刪除時纔會觸發,刪除屬性時不會觸發
print('---->')#文件執行完後,內存釋放,會觸發__del__
__call__對象後面加括號,觸發執行,調用對象類下的__call__方法
class Foo:
def __call__(self, *args, **kwargs):
print('執行__call__')
f1 = Foo()
f1()#執行f1類下的__call__方法
Foo()#執行Foo類下的__call__方法,返回一個實例
__next__和__iter__實現迭代器協議
class Foo:
def __init__(self, n):
self.n = n
def __iter__(self):#__iter__將對象變成一個可迭代的對象(在類中必須有一個__iter__方法,才能將對象變成一個可迭代對象)
return self
def __next__(self):
if self.n == 13:
raise StopIteration('終止了')
self.n += 1
return self.n
f1 = Foo(10)
print(f1.__next__())
print(next(f1))
print(next(f1))
print(next(f1))#會報錯
class Foo:
def __init__(self, n):
self.n = n
def __iter__(self):#__iter__將對象變成一個可迭代的對象(在類中必須有一個__iter__方法,才能將對象變成一個可迭代對象)
return self
def __next__(self):
if self.n == 13:
raise StopIteration('終止了')
self.n += 1
return self.n
f1 = Foo(10)
for i in f1:#iter(f1)→f1.__iter__()
print(i)#不會報錯
迭代器協議實現斐波那契數列
class Fib:
def __init__(self):
self._a = 1
self._b = 1
def __iter__(self):
return self
def __next__(self):
if self._a > 100:
raise StopIteration('終止了')
self._a, self._b = self._b, self._a + self._b
return self._a
f1 = Fib()
print(next(f1))
print(next(f1))
print(next(f1))
print(next(f1))
print('------>')
for i in f1:#for會接着當前位置繼續往下走,不會從頭來過
print(i)
描述符理論
描述符定義:描述符本質就是一個新式類,在這個新式類中,至少實現了__get__(),__set__(),__delete()中的一個,這也被稱爲描述器協議
__get__():調用一個屬性時,觸發
__set__():爲一個屬性賦值時,觸發
__delete():採用del刪除屬性時,觸發
class Foo:
def __get__(self, instance, owner):
print('__get__方法')
def __set__(self, instance, value):
print('__set__方法')
def __delete__(self, instance):
print('__delete__方法')
f1 = Foo()
f1.name = 'cyx'
print(f1.name)
del f1.name#描述符產生的實例進行屬性操做並不會觸發三個方法的執行
class Foo:
def __get__(self, instance, owner):
print('__get__方法')
def __set__(self, instance, value):
print('__set__方法')
def __delete__(self, instance):
print('__delete__方法')
class Bar:
x = Foo()
print(Bar.__dict__)
b1 = Bar()
print(b1.x)#另一個類產生的實例調用時,觸發描述符Foo下的__get__方法
b1.x = 1#觸發描述符Foo下的__set__方法
print(b1.__dict__)
del b1.x#觸發描述符Foo下的__delete__方法
描述符分類:數據描述符和非數據描述符
數據描述符:至少實現了__get__(),__set__()
非數據描述符:沒有實現__set__()
class Foo:
def __get__(self, instance, owner):
print('__get__方法')
def __set__(self, instance, value):
print('__set__方法')
def __delete__(self, instance):
print('__delete__方法')
class Bar:
x = Foo()#一旦定義,後續對x屬性的調用全都去找Foo
def __init__(self, n):
self.x = n
b1 = Bar(10)#self.x = 10,觸發set方法
print(b1.__dict__)
注意事項:
描述符自己應該定義成新式類,被代理的類也應該是新式類
必須把描述符定義成被代理類的類屬性,不能爲定義到構造函數中
class Foo:
def __get__(self, instance, owner):
print('__get__方法')
def __set__(self, instance, value):
print('__set__方法', instance, value)
instance.__dict__['x'] = value
def __delete__(self, instance):
print('__delete__方法')
class Bar:
x = Foo()
def __init__(self, n):
self.x = n
b1 = Bar(10)#self.x = 10,觸發set方法
print(b1.__dict__)
b1.x = 111111
print(b1.__dict__)
b1.y = 'hji'
print(b1.__dict__)#沒有被代理的屬性就按正常操做便可
描述符優先級:
1.類屬性
2.數據描述符
3.實例屬性
4.非數據描述符
5.找不到的屬性觸發__getattr__()
類屬性>數據描述符
class Foo:
def __get__(self, instance, owner):
print('__get__方法')
def __set__(self, instance, value):
print('__set__方法', instance, value)
def __delete__(self, instance):
print('__delete__方法')
class Bar:
x = Foo()
print(Bar.x)#屬性字典裏找x,x被代理,找Foo,觸發__get__
Bar.x =1 #類屬性優先級>描述符,從新定義賦值,和描述符不要緊了,實質是底層字典的覆蓋/修改操做
print(Bar.__dict__)
print(Bar.x)
數據描述符>實例屬性
class Foo:
def __get__(self, instance, owner):
print('__get__方法')
def __set__(self, instance, value):
print('__set__方法', instance, value)
def __delete__(self, instance):
print('__delete__方法')
class Bar:
x = Foo()
print(Bar.__dict__)
b1 = Bar()
b1.x#先在b1的字典裏找x,找不到再去類的字典裏找,類x對應的是描述符對象,觸發__get__方法
b1.x =1 #先在b1的字典裏找x,找不到再去類的字典裏找,類x對應的是描述符對象,觸發__set__方法
print(b1.__dict__)
del b1.x#先在b1的字典裏找x,找不到再去類的字典裏找,類x對應的是描述符對象,觸發__delete__方法
class Foo:
def __get__(self, instance, owner):
print('__get__方法')
def __set__(self, instance, value):
print('__set__方法', instance, value)
def __delete__(self, instance):
print('__delete__方法')
class Bar:
x = Foo()
b1 = Bar()
Bar.x = 187655#描述符被覆蓋
b1.x#再也不調用描述符
del Bar.x
b1.x
實例屬性>非數據描述符
class Foo:
def __get__(self, instance, owner):
print('__get__方法')
class Bar:
x = Foo()
b1 = Bar()
# b1.x#先在b1的字典裏找x,找不到再去類的字典裏找,類x對應的是描述符對象,觸發__get__方法
b1.x =1
print(b1.__dict__)
數據描述符>實例屬性
class Foo:
def __get__(self, instance, owner):
print('__get__方法')
def __set__(self, instance, value):
pass
class Bar:
x = Foo()
b1 = Bar()
# b1.x#先在b1的字典裏找x,找不到再去類的字典裏找,類x對應的是描述符對象,觸發__get__方法
b1.x =1
print(b1.__dict__)
找不到觸發__getattr__
class Foo:
def __get__(self, instance, owner):
print('__get__方法')
def __set__(self, instance, value):
pass
class Bar:
x = Foo()
def __getattr__(self, item):
print('------>')
b1 = Bar()
b1.x =1
print(b1.__dict__)
b1.xxxxxxxxxxxxx
#自省
hasattr(obj,'屬性') #obj.屬性 是否存在
getattr(obj,'屬性') #獲取obj.屬性 不存在則報錯
getattr(obj,'屬性','默認值') #獲取obj.屬性 不存在不會報錯,返回那個默認值
setattr(obj,'屬性','屬性的值') #obj.屬性=屬性的值
delattr(obj,'屬性') #del obj.屬性
#__getattr__,__setattr__,__delattr__
obj點的方式去操做屬性時觸發的方法
__getattr__:obj.屬性 不存在時觸發
__setattr__:obj.屬性=屬性的值 時觸發
__delattr__:del obj.屬性 時觸發
#__getitem__,__setitem_,__delitem__
obj[‘屬性’]的方式去操做屬性時觸發的方法
__getitem__:obj['屬性'] 時觸發
__setitem__:obj['屬性']=屬性的值 時觸發
__delitem__:del obj['屬性'] 時觸發
#__get__,__set__,__delete__
描述就是一個新式類,這個類至少要實現上述三個方法的一個
class 描述符:
def __get__():
pass
def __set__():
pass
def __delete__():
pass
class 類:
name=描述符()
obj=類()
obj.name #get
obj.name='egon' #set
del obj.name #delete
#__del__:析構方法
垃圾回收時觸發
上下文管理協議
class Foo:
def __init__(self, name):
self.name = name
def __enter__(self):
print('執行enter')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('執行exit')
print(exc_type)
print(exc_val)
print(exc_tb)
with Foo('a.txt') as f:#with Foo('a.txt')觸發__enter__,__enter__返回的值賦值給f
print(f)
print(f.name)#無異常時代碼塊執行完畢時觸發__exit__
print('5555')
class Foo:
def __init__(self, name):
self.name = name
def __enter__(self):
print('執行enter')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('執行exit')
print(exc_type)#異常類
print(exc_val)#異常值
print(exc_tb)#異常traceback
with Foo('a.txt') as f:#with Foo('a.txt')觸發__enter__,__enter__返回的值賦值給f
print(f)
print(ahdkhjjv)#有異常時直接執行__exit__,後面代碼再也不執行
print(f.name)
print('5555')
class Foo:
def __init__(self, name):
self.name = name
def __enter__(self):
print('執行enter')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('執行exit')
print(exc_type)#異常類
print(exc_val)#異常值
print(exc_tb)#異常traceback
return True#將異常吸取掉了,with正常結束掉
with Foo('a.txt') as f:#with Foo('a.txt')觸發__enter__,__enter__返回的值賦值給f
print(f)
print(ahdkhjjv)#有異常時直接執行__exit__,後面代碼再也不執行,除非在__exit__中return True
print(f.name)
print('1111')
print('5555')
with obj as f:
'代碼塊'
1.with obj ----》觸發obj.__enter__(),拿到返回值
2.as f----->f=返回值、
3.with obj as f 等同於 f=obj.__enter__()
4.執行代碼塊
一:沒有異常的狀況下,整個代碼塊運行完畢後去觸發__exit__,它的三個參數都爲None
二:有異常的狀況下,從異常出現的位置直接觸發__exit__
a:若是__exit__的返回值爲True,表明吞掉了異常
b:若是__exit__的返回值不爲True,表明吐出了異常
c:__exit__的的運行完畢就表明了整個with語句的執行完畢
描述符應用
用描述符爲Python加上類型檢測
class Typed:
def __init__(self, key, expected_type):
self.key = key
self.expected_type = expected_type
def __get__(self, instance, owner):
print('get方法')
# print('instance參數【%s】' %instance)
# print('owner參數【%s】' % owner)
return instance.__dict__[self.key]
def __set__(self, instance, value):
print('set方法')
# print('instance參數【%s】' % instance)
# print('value參數【%s】' % value)
if not isinstance(value, self.expected_type):
raise TypeError('%s傳入的類型不是%s' %(self.key, self.expected_type))
instance.__dict__[self.key] = value
def __delete__(self, instance):
print('delete方法')
# print('instance參數【%s】' % instance)
instance.__dict__.pop(self.key)
class People:
name = Typed('name', str)
age = Typed('age', int)
def __init__(self, name, age, salary):
self.name = name
self.age = age
self.salary = salary
p1 = People('cyx', '12', 15.9)
print(p1.__dict__)
# p1.name
# p1.name = 'ypp'
# print(p1.__dict__)
類的裝飾器的基本原理
def deco(obj):
print('=====')
obj.x = 1
obj.y = 2
obj.z = 3
return obj
@deco #Foo = deco(Foo)
class Foo:
pass
print(Foo.__dict__)
def Typed(**kwargs):
def deco(obj):
for key, val in kwargs.items():
setattr(obj, key, val)
return obj
return deco
@Typed(x=1, y=2, z=3)
class Foo:
pass
print(Foo.__dict__)
@Typed(name = 'cyx')
class Bar:
pass
print(Bar.__dict__)
經過裝飾器給類加上描述符
class Typed:
def __init__(self, key, expected_type):
self.key = key
self.expected_type = expected_type
def __get__(self, instance, owner):
print('get方法')
# print('instance參數【%s】' %instance)
# print('owner參數【%s】' % owner)
return instance.__dict__[self.key]
def __set__(self, instance, value):
print('set方法')
# print('instance參數【%s】' % instance)
# print('value參數【%s】' % value)
if not isinstance(value, self.expected_type):
raise TypeError('%s傳入的類型不是%s' %(self.key, self.expected_type))
instance.__dict__[self.key] = value
def __delete__(self, instance):
print('delete方法')
# print('instance參數【%s】' % instance)
instance.__dict__.pop(self.key)
def deco(**kwargs):
def wrapper(obj):
for key, val in kwargs.items():
setattr(obj, key, Typed(key, val))
return obj
return wrapper
@deco(name = str, age = int, salary = float)
class People:
def __init__(self, name, age, salary):
self.name = name
self.age = age
self.salary = salary
p1 = People('cyx', 12, 15.9)
print(People.__dict__)
利用描述符自定製property&實現延遲計算功能
class Lazyproperty:
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):#類調用時,傳入的instance是None
print('get')
if instance is None:
return self
res = self.func(instance)
setattr(instance, self.func.__name__, res)
return res
# def __set__(self, instance, value):一旦加上它變成數據描述符,就沒法實現延時計算,由於數據描述符優先級高於實例屬性
# pass
class Room:
def __init__(self, name, length, width):
self.name = name
self.length = length
self.width = width
@Lazyproperty #area = Lazyproperty(area),實現了給類增長描述符的功能
def area(self):
return self.length * self.width
@property
def area1(self):
return self.length * self.width
r1 = Room('restroom', 3, 8)
print(r1.area)#調用area會觸發代理Lazyproerty的__get__方法
print(r1.__dict__)
print(r1.area)#當實例屬性中存在area屬性時,再也不去找非數據描述符
print(r1.area)
print(r1.area)
print(Room.area)
class Foo:
@property
def AAA(self):
print('調用時運行')
@AAA.setter
def AAA(self, val):
print('設置時運行')
@AAA.deleter
def AAA(self):
print('刪除時運行')
#只有在屬性AAA定義property後,才能定義AAA.setter,AAA.deleter
f1 = Foo()
f1.AAA
f1.AAA = 11
class Foo:
def get_AAA(self):
print('調用時運行')
def set_AAA(self, val):
print('設置時運行')
def del_AAA(self):
print('刪除時運行')
AAA = property(get_AAA, set_AAA, del_AAA)#get, set, delete順序不能變
f1 = Foo()
f1.AAA
f1.AAA = 11
del f1.AAA
#type方法建立類
def __init__(self, name, age):
self.name = name
self.age = age
def test(self):
print('------')
res = type('Foo', (object,), {'x': 1, '__init__': __init__, 'test': test})
print(res)
print(res.__dict__)
f1 = res('cyx', 18)
print(f1.name)
f1.test()
自定義元類
class MyType(type): def __init__(self, a, b, c): print('元類的構造函數') def __call__(self, *args, **kwargs): obj = object.__new__(self) self.__init__(obj, *args, **kwargs) return objclass Foo(metaclass=MyType):#執行Foo = MyType(Foo,'Foo', (object,), {}) def __init__(self, name): self.name = namef1 = Foo('cyx')#觸發__call__執行print(f1.name)