目錄:1.面向過程與面向對象 2.類與對象 3. 繼承 4.組合 5.接口 6.抽象類 7.多態 8.封裝 9.property 10.綁定方法 11.__str__方法 12.反射 13.__attr__方法 14.__item__方法 15.元類 16.異常處理java
編程範式:面向對象、面向過程等不一樣的編程風格python
面向過程的程序設計linux
核心是‘過程’,流水線式的編程,在設計程序時須要把整個流程設計出來git
優勢:1.體系結構更加清晰 2.簡化程序編程的複雜度編程
缺點:可擴展性極其的差c#
因此說面向過程的程序設計應用於那些功能一旦實現以後就不多須要改變的場景編程語言
例如:linux內核,git,Apache HTTP Server等函數
面向對象的程序設計工具
核心是‘對象’,python中一切皆對象this
優勢:解決了程序擴展性低的問題,對某一個對象單獨修改,會馬上反映到整個體系中
缺點:編程複雜度高於面向過程,可控性差,沒法像面向過程的程序設計那樣精準的預測結果
通常應用於需求常常變化的軟件中,互聯網應用、企業內部軟件、遊戲等
面向對象的三大特性:
繼承、多態與封裝
類與對象
對象是特徵與技能的結合體,而類就是一系列對象類似的特徵與技能的結合體
現實生活中,先有對象後又類,可是在程序中,務必保證先定義類,後產生對象
調用類會產生對象,返回的就是對象
類名最好首字母大寫
類在定義階段,其代碼就會執行,而函數不是這樣的,函數在定義階段代碼不會執行
用class關鍵字定義類,特徵用變量標識,技能用函數標識
class Garen:
camp='Demacia' #數據屬性
def __init__(self,name):
self.Name = name #這裏其實就是 對象.Name = name,調用對象查看的時候須要 對象.Name
def attack(self): #函數屬性
print('attack')
類的實例化:類名() Garen(),獲得對象,類中的數據屬性是全部對象共有的,技能(函數屬性)是綁定到對象的方法
類的屬性引用:變量與函數均可以用類名.的方式調用 Garen.camp Garen.attack()
類的使用:
增:Garen.x = 1
刪:del Garen.x
改:Garen.camp = 'Nuoxs'
查:Garen.camp 使用.的方式查看
__init__方法:該方法是在對象產生以後纔會執行的,只用來爲對象進行初始化操做
用來爲對象定製對象本身獨有的特徵
能夠有任意代碼,但必定不能有返回值
加上__init__方法後,實例化的步驟是:
先產生一個空對象
再把空對象和須要給__init__傳的值傳進去,空對象就是第一個值self
g = Garen('111')這樣就實例化出了一個對象,對象自己只有用__init__定義的數據屬性,組成一個字典
對象除了用__init__定義的獨有特徵,還有類中的數據屬性和函數屬性,這兩部分不是對象自己的,是類裏面的
對象的使用:
增: g.sex = 'male'
刪: del g.Name
改:g.Name = '222'
查:g.Name
類和對象的名稱空間用__dict__查看,是以字典的形式存儲的,能夠用字典的方法訪問其中的內容
Garen.__dict__ g.__dict__,這個只能查看到對象自定製的__init__的內容,也是字典形式
類有兩種屬性:數據屬性與函數屬性
數據屬性是全部對象共享的
函數屬性是綁定給對象用的,稱爲綁定到對象的方法
綁定方法的核心在於‘綁定’,綁定給誰就由誰來調用,就會把‘誰’當作第一個參數傳給方法,
即自動傳值,因此雖然函數的功能是同樣的,可是不一樣對象調用,結果不一樣
實例:
class Student:
school = 'luffycity'
count = 0
def __init__(self, name, age, sex):
self.Name = name
self.Age = age
self.Sex = sex
Student.count += 1 #調用類的count完成自增一操做,才能保存到類
def learn(self):
print('%s is learning' % self.Name)
stu1 = Student('a','2',3)
stu2 = Student('b','2',3)
stu3 = Student('c','2',3)
print(Student.count)
定義學生類,產生學生對象,並加上計數器,計算出實例化了幾個對象
isinstance與issubclass
isinstance(obj,cls)檢查obj是不是類cls的對象
issubclass(sub,super)檢查sub類是不是super類的派生類
繼承
繼承指的是類與類之間的關係,是一種什麼‘是’什麼的關係,繼承的功能之一就是解決代碼重用
繼承是一種建立新類的方法,在python中,新建的類能夠繼承一個或多個父類
父類又能夠稱爲基類或超類,新建的類稱爲派生類或子類,多繼承時用,逗號隔開
能夠簡單理解爲,類似的函數與變量抽象出類,類似的類抽象出父類
類名.__base__查看從左到右繼承的第一個子類
類名.__bases__查看全部繼承的父類
在python3中,全部的類都是新式類,默認繼承object,經典類沒有繼承object以及object的子類,經典類只在python2中有
先抽象再繼承
繼承是基於抽象的結果,經過編程語言去實現它,
確定要先經歷抽象這個過程,才能經過繼承的方式去表達抽象關係
派生:
子類添加本身新的屬性或者在本身這裏從新定義屬性,不會影響到父類
一旦從新定義的屬性與父類中重名,那麼調用時以本身爲準
子類中調用父類的方法:在子類中重用父類的方法或屬性
在子類中,新建的重名函數屬性,在編輯函數內功能時,有可能用到父類中重名函數的功能
方法一:指名道姓,用調用普通函數的方式,即父類類名.func()的方式,self仍是要寫的,該方法不依賴於繼承
方法二:用super函數,即super().func()的方式,這樣寫func的括號內不用寫self,該方法依賴於繼承,但這種方式只能用於新式類
super(子類類名,self).func() 這樣調用的func就是綁定方法了,這是python2中的寫法
在python3中,super()括號內能夠不用寫,就直接super().func()這樣就能夠了
繼承實現的原理
1.python的類能夠繼承多個,java和c#只能繼承一個
2.當類是新式類時,多繼承狀況下,會按照廣度優先的方式查找,即python3是廣度優先
廣度優先就是先走各條分支,在最後一條分支再完全走到最後去查找最頂級的父類
3.當類是經典類時,多繼承狀況下,會按照深度優先的方式查找
深度優先就是先把一條分支走到最後,再依次走其餘的分支
簡單講就是一條路走到黑
繼承的兩種用途
1.繼承基類的方法,而且作出本身的改變或擴展:實踐中,繼承的這種用途意義並不很大
甚至經常有害,由於繼承使得子類與基類出現強耦合
2.聲明某個子類兼容於某基類,定義一個接口類(模仿java的Interface),接口類中定義了一些接口名
接口名就是函數名,且並未實現接口的功能,子類繼承接口類,並實現接口中的功能
繼承實例:
class A:
def f1(self):
print('from A')
super().f1()
class B:
def f1(self):
print('from B')
class C(A,B):
pass
c = C()
c.f1()
結果:
from A
from B
核心點在於super()是一個依賴於繼承的方法
解釋以下:(方法解析順序)MRO列表爲[C,A,B,object]先在對象c這裏找f1,c自己沒有,再去c的類C裏找,C也沒有,
再去父類A中找,找到f1函數執行,先打印from A,而後走到super().f1()這一步,疑惑點就在這裏,
由於父類A繼承object類,而object類沒有f1方式,感受應該是要報錯的,可是不是這樣的,
在此處,執行的是對象c,那就按照對象c的繼承關係MRO列表走,父類A走到super那一步,須要找一個f1方法,
就再按照MRO列表的順序,去另一個父類B中找,找到f1方法,打印from B
組合
在一個類中以另一個類的對象做爲數據屬性,這稱爲類的組合
繼承是一種‘是’的關係,組合是一種‘有’的關係
當類之間有顯著不一樣,而且較小的類是較大的類所須要的組件時,用組合比較好
實例:
class Teacher:
def __init__(self,name,sex,course,birth):
self.name=name
self.sex=sex
self.course=course
self.birth=birth #組合
class Student:
def __init__(self,name,sex,course):
self.name=name
self.sex=sex
self.course=course #組合
class Course:
def __init__(self,name,price,peroid):
self.name=name
self.price=price
self.period=peroid
class Birth:
def __init__(self,date):
self.date=date
birth_own=Birth('19900101')
python_obj=Course('python','15800','7m')
t1=Teacher('egon','male',python_obj,birth_own) 把Course與Birth實例化出來的兩個對象傳進去
s1=Student('cobila','male',python_obj)
print(t1.birth.date)
繼承與組合是兩種解決代碼重用的方案
接口
接口提取了一羣類共同的函數,能夠把接口當作一個函數的集合,而後讓子類去實現接口中的函數
其意義在於歸一化,歸一化的好處:
1.讓使用者無需關心對象的類是什麼,只須要知道這些對象都具有哪些功能就能夠了,下降使用難度
2.使得高層的外部使用者能夠不加區分的處理接口兼容的對象集合
抽象類
與java同樣,python也有抽象類的概念,但一樣須要藉助模塊實現
抽象類是一個特殊的類,特殊之處在於只能被繼承,不能被實例化,且子類必須實現抽象方法
若是說類是從一堆對象中抽象出來的,那麼抽象類就是從一堆類中抽象出來的,包含數據屬性和函數屬性
與繼承有一個本質的區別,就是繼承抽象類後,抽象類中定義的函數必須實現,這個是硬性規定,而普通繼承就算不實現也沒事
這樣實現歸一化,統一標準,下降使用者的使用複雜度
import abc 利用abc模塊實現抽象類
#抽象類:本質仍是類,與普通類的區別在於,加了裝飾器的函數,子類必須實現他們
class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod
def run(self):
pass
@abc.abstractmethod
def speak(self):
pass
這樣寫完後,類Animal就是一個抽象類,加了裝飾器的兩個函數run和speak比較重要
由於在用子類繼承抽象類的時候,子類的函數必需要定義這兩個函數,去實現這兩個函數的功能
抽象類與接口
抽象類的本質仍是類,是一組類的類似性,包括數據屬性和函數屬性,而接口只強調函數屬性的類似性
抽象類是一個介於類和接口之間的概念,同時具有類和接口的部分特性,能夠用來實現歸一化設計
多態與多態性
多態是指同一類事物有多種形態
多態性是指在不考慮實例類型的狀況下使用實例
靜態多態性:如任何類型均可以用運算符+進行運算
動態多態性:不須要去考慮對象具體是什麼類型,就能夠直接去使用,而且能夠進一步去定義統一的接口
python自己就是支持多態性的
多態性增長了程序的靈活性與可擴展性
多態性依賴於繼承,能夠定義統一的接口,傳入不一樣類型的值
雖然調用邏輯是同樣的,但執行結果卻不同
python崇尚的是‘鴨子類型’,而且實現的也是‘鴨子類型’,不一樣的對象不須要有繼承關係,甚至能夠徹底無關,只須要看起來像,
外觀上行爲上看起來像,就能夠認爲是同一類
封裝
在python中,用雙下劃線開頭的方式將屬性隱藏起來,設置成私有的
類中全部雙下劃線開頭的名稱如__x都會自動變造成 _類名__x的形式,在類的定義階段就會發生變形
必定要注意是在類定義階段發生變形,若是實例化出來對象再添加屬性,那麼屬性名就算以雙下劃線開頭也不會變形
這種操做並非嚴格意義上的限制外部訪問,僅僅是語法意義上的變形,用類名._類名__x的方式依然能夠訪問獲得
這種自動變形的特色:
1.類中定義的__x只能在內部使用,在內部能夠經過 __x的形式訪問,在外部以及對象中不能夠,須要經過 _類名__x的形式來調用
2.這種變形其實正是針對外部的變形,外部沒法經過__x訪問到
3.雙下劃線開頭的屬性在繼承給子類時,子類是沒法覆蓋的
封裝不是單純意義的隱藏
1.封裝數據,明確的區份內外,可是將數據隱藏起來不是目的,隱藏起來後再對外提供操做該數據的接口,
而後咱們能夠在接口附加上對該數據操做的限制,以此完成對數據屬性操做的嚴格控制
2.封裝方法,目的是隔離複雜度
特性property
property自己是一個函數,用法就是裝飾器用法,就像是作了一個假裝
property是一種特殊的屬性,訪問它時會執行一段功能(函數)而後返回值
簡單講,我的理解就是,用property裝飾一個函數,被裝飾的函數能夠像調用數據屬性那樣去調用
即 對象名.函數名 的方式,不用加括號,看起來像是調用數據屬性,而不是函數屬性
那是否是必須跟return連用呢?否則調用沒有結果呀
實例:
import math
class Circle:
def __init__(self,radius):
self.radius=radius
@property
def area(self):
return math.pi*self.radius**2
@property
def perimeter(self):
return 2*math.pi*self.radius
c=Circle(7)
print(c.area)
print(c.perimeter)
函數area與函數perimeter都被property裝飾過,在調用的時候就是c.area和c.perimeter
外觀上看起來像是調用數據屬性
被property修飾的函數還有函數名.setter和函數名.deleter兩個裝飾器方法
即:
@area.setter #這個就是賦值,c.area = 實現賦值修改操做,不寫這個方法就不能進行賦值操做
def area(self,value):
函數體,進行的是修改值的操做
@area.deleter #這個就是刪除操做,del c.area 實現刪除操做,不寫這個方法不能進行刪除操做
def area(self):
函數體,進行的是刪除的操做
綁定方法
類中定義的函數分爲兩大類
一:綁定方法(綁定給誰,誰來調用,就自動將其自己當作第一個參數傳入)
1.綁定到類的方法:用classmethod裝飾器裝飾的方法,由類來調用,自動將類當作第一個參數傳入
類.綁定到類的方法()調用執行,對象也能夠調用但依然會把類當作第一個參數傳入
實例:
import time
class Date:
def __init__(self,year,month,day):
self.year=year
self.month=month
self.day=day
@classmethod
def now(cls): #這個cls,是哪一個類來調用該方法,就把哪一個類當作cls傳進去
t=time.localtime()
return cls(t.tm_year,t.tm_mon,t.tm_mday)
@classmethod
def tomorrow(cls):
t=time.localtime(time.time()+86400)
return cls(t.tm_year,t.tm_mon,t.tm_mday)
class EuroDate(Date):
def __str__(self):
return '年:%s,月:%s,日:%s'%(self.year,self.month,self.day)
e1=EuroDate.now()#這裏是EuroDate這個類調用了now方法,now方法是被classmethod裝飾的,因此,
# now如今是綁定到EuroDate的方法
e2=EuroDate.tomorrow() #這個也跟now同樣,綁定到EuroDate的方法
#其實在這裏,e一、e2這兩個對象都由於調用了綁定到類的方法的緣由,函數內部定義的代碼有傳值的功能
#因此實例化出來的這兩個對象無需再傳值,而下面的e3就必需要手動傳值
#若是實例化類Date,不調用now和tomorrow這兩個方法,那也是要手動傳值的
e3 = EuroDate(2018,5,9)
print(type(e1))
print(e2)
print(e3) #打印,觸發__str__的執行
print(EuroDate.now)
結果:
<class '__main__.EuroDate'>
年:2018,月:5,日:10
年:2018,月:5,日:9
<bound method Date.now of <class '__main__.EuroDate'>>
2.綁定到對象的方法:沒有被任何裝飾器裝飾的方法
自動把對象當作第一個參數傳入
在類中定義的函數,只要是沒有被裝飾器裝飾的,都是綁定到對象的方法,均可以自動傳值
二:非綁定方法:用staticmethod裝飾器裝飾的方法
不依賴於類也不依賴於對象
不與類或對象綁定,類或對象均可以調用,但沒有自動傳值,就是一個普通的工具
__str__
一個定義在類的內部的方法,能夠返回一個字符串類型,與return連用
何時觸發呢?打印由這個類產生的對象時,觸發執行
實例:
class People:
def __init__(self,name,age):
self.name=name
self.age=age
def __str__(self):
return '<name:%s,age:%s>'%(self.name,self.age)
p1=People('egon',18)
print(p1)
改變對象的字符串顯示__str__ __repr__ 這兩個函數的返回值都必須是字符串,不然拋出異常
自定製格式化字符串__format__
反射
反射的概念是由Smith在1982年提出的,主要是指程序能夠訪問、檢測和修改它自己狀態或行爲的一種能力
亦稱爲自省
python面向對象中的反射:
經過字符串的形式操做對象相關的屬性,適用於對象適用於類,其實由於python中一切事物都是對象,均可以使用反射
python中一切皆對象
四個能夠實現自省的函數
hasattr(object,name) 判斷object中有沒有一個name字符串對應的方法或屬性,返回布爾值
getattr(object,name) 獲取object中name字符串對應的方法或屬性,若是該方法或屬性不存在就會報錯
能夠在括號裏再加一個值來避免報錯,好比(object,name,'not exist')
這樣不存在也不會報錯,而是返回‘not exist’
setattr(object,name,value) 設置屬性,(對象,屬性名稱,屬性對應的值) (obj,name,value),能夠新增,能夠修改
delattr(object,name) 刪除屬性,不存在會報錯,存在返回None (obj,name)
實例:
def add():
print('add')
def change():
print('change')
def search():
print('search')
def delete():
print('delete')
import sys
this_module=sys.modules[__name__]
while True:
cmd=input('>>:').strip()
if not cmd:continue
if hasattr(this_module,cmd):
func=getattr(this_module,cmd)
func()
這樣就用反射實現了接收用戶輸入的字符串,來實現對應的功能
爲何用反射之反射的好處:
1.實現可插拔機制,反射的好處就是能夠實現定義好接口,接口只有在被完成後纔會真正執行
這就實現了即插即用,這實際上是一種‘後期綁定’,即你能夠事先把主要邏輯寫好,
只定義接口,而後後期再去實現接口的功能
2.動態導入模塊(基於反射當前模塊),經過字符串導入模塊,使用importlib
import importlib
t=importlib.import_module('time')
print(t.time())
__attr__系列
__setattr__ 賦值、添加或修改屬性會觸發它的執行
__getattr__ 只有在使用點調用屬性且屬性不存在時纔會觸發
__delattr__ 刪除屬性時會觸發
class Foo:
def __init__(self,name):
self.name=name
def __setattr__(self, key, value):
if not isinstance(value,str):
raise TypeError('must be str')
print('----setattr---key:%s,value:%s'%(key,value))
self.__dict__[key]=value 這裏若是寫self.key=value就會出現無限遞歸,須要去操做字典
def __delattr__(self, item):
print('delete:%s'%item)
del self.__dict__[item] 這裏跟上面__setattr__同樣,須要操做字典
#或者 self.__dict__.pop(item)
f1=Foo('egon') 會觸發__setattr__
f1.age='18' 會觸發__setattr__
print(f1.__dict__)
class Foo1:
def __init__(self,x):
self.name=x
#屬性不存在時纔會觸發執行
def __getattr__(self, item):
print('getattr-->%s %s'%(item,type(item)))
f=Foo1('egon')
print(f.xxx) f.xxx這個操做自己就會觸發__getattr__,再把這個操做print的話會再打印其返回值
由於沒有寫return,因此返回值是None
__item__系列,自動觸發執行的,不用手動調用obj.__items__
__setitem__
__getitem__
__delitem__
把對象操做屬性的形式模擬成字典的格式,調用時字典內必須是字符串的格式
__setattr__,__getsttr,__delsttr__調用時用的是f.name這樣的格式
__setitem__,__getitem__,__delitem__調用時用的是f['name']這樣的格式
實例:
class Foo:
def __init__(self,name):
self.name=name
def __getitem__(self, item):
print('getitem')
#return self.__dict__[item] #若是屬性不存在就會報錯
return self.__dict__.get(item) #應該這樣寫纔不會報錯
def __setitem__(self, key, value):
print('setitem')
self.__dict__[key]=value
def __delitem__(self, key):
print('del obj[key]時,我執行')
self.__dict__.pop(key)
f=Foo('egon')
print(f.name)
f['age']=18 #這種方式模擬一個字典的格式,觸發__setitem__的執行
print(f.__dict__)
print(f['name']) #觸發__getitem__的運行
元類
元類是一個比較深層次的內容,平時可能比較少用獲得,但不是說不重要
exec方法
三個參數:
1.字符串形式的命令
2.全局做用域(字典形式),若是不指定默認就使用globals()
3.局部做用域(字典形式),若是不指定默認就使用locals()
python中一切皆對象,對象能夠怎麼用?
1.能夠被引用
2.能夠當作函數的參數傳入
3.能夠當作函數的返回值
4.能夠當作容器類的元素
類自己也是一個對象,當使用關鍵字class的時候,python解釋器
在加載class的時候就會建立一個對象(這裏的對象指的是類而非類的實例),
於是咱們能夠將類當作一個對象去使用,一樣知足第一類對象的概念
類的第一類對象概念:能夠把類賦值給一個變量,能夠把類做爲函數參數進行傳遞
能夠把類做爲函數的返回值,能夠在運行時動態地建立類
元類是類的類,是類的模板,元類是用來控制如何建立類的,正如類是建立對象的模板同樣,
元類的主要目的是爲了控制類的建立行爲,元類的實例化的結果是咱們用class定義的類,
而類的實例爲對象
建立類的兩種方式:
1.使用class關鍵字,其元類都是type
2.手動模擬class建立類的過程,利用type關鍵字模擬,將建立類的步驟拆分開,手動去建立
類的三要素:類名、類的基類、類的名稱空間
def run(self):
print('%s is runing'%self.name)
class_name='Bar' #類名
bases=(object,)#繼承關係,這裏用的是元組
class_dic={
'x':'1',
'run':run
} #字典 類的局部名稱空間
Bar=type(class_name,bases,class_dic) #用type的方式去本身定義類,這三個的順序必須這樣
print(Bar)
type接收三個參數type(class_name,bases,class_dic)
第一個參數是字符串,表示類名
第二個參數是元組,(object,),表示全部的父類
第三個是字典,若是是空字典就表示沒有定義屬性與方法
type建立類實例:
class_name = 'Chinese'
class_bases = (object,)
class_body = """
country = 'China'
def __init__(self,name,age):
self.name = name
self.age = age
def talk(self):
print('%s is talking' % self.name)
"""
class_dic = {}
exec(class_body,globals(),class_dic) #這個地方用exec就是爲了把函數體轉成字典的格式
print(class_dic)
Chinese = type(class_name,class_bases,class_dic)
print(Chinese)
自定製元類
產生新的對象 = object.__new__(繼承 object 類的子類,寫什麼類均可以)
實例:
class Mymeta(type):
#控制類的建立
def __init__(self,class_name,class_bases,class_dic): #這個在實例化的時候自動執行調用,因此
for key in class_dic: #能夠在這裏自定製加限制條件,能夠限制實例化出來的類名、註釋等
if not callable(class_dic[key]):continue
if not class_dic[key].__doc__:
raise TypeError('no doc')
super(Mymeta,self).__init__(class_name,class_bases,class_dic)
#控制類的實例化
def __call__(self, *args, **kwargs):
#該方法內要作三件事:造一個空對象、初始化空對象、返回初始化好了的空對象
obj=object.__new__(self) #實例化類,產生一個新的空對象
self.__init__(obj,*args,**kwargs) #調用類下面的__init__方法,初始化空對象
return obj #返回初始化好了的新的空對象
# type.__init__(self,class_name,class_bases,class_dic)
這裏這個__call__能夠這樣理解:Foo是經過元類Mymeta實例化出來的類,那Foo就是一個對象
對象()這樣調用的就是其元類中的__call__方法,若是自定義的元類中不寫__call__方法,就默認使用object中的__call__方法
可是若是本身定義__call__方法的話,該方法中就須要作三件事:造一個空對象、初始化空對象、返回初始化好了的空對象
這樣的話,就能夠用過__call__方法實現控制實例化了
class Foo(metaclass=Mymeta): #metaclass是英文元類的意思
x=1
def __init__(self,name):
'__init__'
self.name=name
def run(self):
'run function'
print('runing')
f=Foo('egon')
print(f)
print(f.name)
單例模式:
1.
2.元類的方式
元類相關練習題:
1.
#在元類中控制把自定義類的數據屬性都變成大寫
class Mymetaclass(type):
def __new__(cls,name,bases,attrs):
update_attrs = {}
for k,v in attrs.items():
if not callable(v) and not k.startswith('__'):
update_attrs[k.upper()] = v
else:
update_attrs[k] = v
return type.__new__(cls,name,bases,update_attrs)
class Chinese(metaclass=Mymetaclass):
country = 'china'
tag = 'legend of the Dragon'
def walk(self):
print('%s is walking'%self.name)
print(Chinese.__dict__)
2.
# 在元類中控制自定義的類無需init方法
# 1.元類幫其完成建立對象,以及初始化操做
# 2.要求實例化時傳參必須爲關鍵字形式,不然拋出異常TypeError:must use keyword argument
# 3.key做爲用戶自定義類產生對象的屬性,且全部屬性變成大寫
class Mymetaclass(type):
def __call__(self, *args, **kwargs):
if args:
raise TypeError('must use keyword argument for key function')
obj = object.__new__(self)
for k,v in kwargs.items():
obj.__dict__[k.upper()] = v
return obj
class Chinese(metaclass=Mymetaclass):
country = 'China'
tag = 'legend of the Dragon'
def walk(self):
print('%s is walking'%self.name)
p = Chinese(name = 'egon',age = 18,sex = 'male')
print(p.__dict__)
異常處理
異常就是程序運行時發生錯誤的信號,在程序出現錯誤時,則會產生一個異常
若程序沒有處理它,則會拋出該異常,程序的運行也隨之終止
異常分爲三部分:
1.追蹤信息
2.異常類型
3.異常的值
錯誤分兩種:
1.語法錯誤,過不了python解釋器的語法檢測,必須在程序執行前就改正
2.邏輯錯誤
TypeError 傳入對象類型與要求的不符合
NameError 使用一個還未被賦予對象的變量
ValueError 傳入一個調用者不指望的值,即便值的類型是正確的
IndexError 下標索引超出序列邊界,若是x只有三個元素,卻試圖訪問x[5]
IndentationError 語法錯誤的子類,代碼沒有正確對齊
KeyError 試圖訪問字典裏不存在的鍵
KeyboardInterrupt Ctrl+C被按下
IOError 輸入/輸出異常,基本上是沒法打開文件
AttributeError 試圖訪問一個對象沒有的屬性,好比foo.x,而x屬性不存在
ImportError 沒法引入模塊或包,基本上是路徑問題或名稱錯誤
SyntaxError python代碼非法,代碼不能被編譯
UnboundLocalError 試圖訪問一個還未被設置的局部變量
基本上是因爲另有一個同名的全局變量,致使你覺得正在訪問它
若是對於錯誤發生的條件是可預知的,能夠用if處理,進行預防
AGE = 10
while True:
age = input('>>:').strip()
if age.isdigit():
age = int(age)
if age == AGE:
print('you are right')
break
若是錯誤發生的條件不可預知,則須要用try...except:在錯誤發生以後進行處理
try:
被檢測的代碼塊
except 異常類型:
try中一旦檢測到異常,就執行這個位置的代碼
try:
print('----')
print(x) 這裏拋異常,try裏面的代碼,異常位置以後的都再也不執行
print('----')
print('----')
print('----')
except KeyError as x:
print(x)
except NameError as e: #把異常類型的值賦值給e
print('>>>',e) 這個程序的異常是x未定義,是NameError,因此這裏執行
except Exception as g: 這個是萬能異常
print(g)
else:
print('try內的代碼塊沒有異常纔會執行')
finally:
print('不管有沒有異常,都會執行,一般是進行清理工做')
多分支:應用於被檢測的代碼塊拋出的異常有多種可能性,而且須要針對每一種異常類型都定製專門的處理邏輯
萬能異常:應用於被檢測的代碼塊拋出的異常有多種可能性,而且針對全部的異常類型都只用一種處理邏輯
#自定義異常
class EgonException(BaseException):
def __init__(self,msg):
self.msg=msg
def __str__(self):
return self.msg
raise EgonException('類型錯誤')
斷言:assert
assert 1==1
assert 1==2就會報錯,AssertionError