在此感謝前輩們的指導:http://python.jobbole.com/80955/
https://www.cnblogs.com/wupeiqi/p/4766801.html
https://www.cnblogs.com/paomaliuju/p/5122761.html
https://www.cnblogs.com/goser/articles/7097728.html
http://www.cnblogs.com/alex3714/articles/5213184.html
https://www.cnblogs.com/cenyu/p/5713686.html
本文是各篇文章優點的綜合,適合有一定基礎的朋友進行個人知識梳理
為了表示對前輩們的尊敬,以下均為純手打,代碼已經在2.7和3.6.3版本的python中測試過,并進行了修改,和注釋整理,以確保最通俗易懂。
首先回顧一下上節知識點
1,面向對象是一種很重要的編程方式,此編程方式的實現主要是通過類和對象的使用來實現的
2,在類中儲存有多個需要的函數
3,對象,使用類這個模板,來進行更加具體的實例化,主要用于調用被包裝在類中的函數。
4,面向對象三大屬性:封裝,繼承和多態
5,python中新式類和經典類的粗淺比較用法
6,深度優先和廣度優先的第一核心概念解釋以及第二爬蟲中具體運用的解釋。
那么這次分享,將會是如下部分:
面向對象高級語法部分
經典類vs新式類核心
字段,屬性
靜態方法、類方法、屬性方法
類的特殊方法
反射
謝謝大家啦(微笑)。
第一部分:
五星級:經典類和新式類的比較用法(重要)
(注:經典類的代碼有的需要在2.7中運行,請提前下好)
在python2及以前的版本中,由任意內置類型派生出來的類(只要有一個內置類型位于類樹的某個位置)都屬于新式類,反之,即不由任意內置類型派生出來的類,則稱之為“經典類”。
關于內置類型的名詞解釋:
新式類”和“經典類”的區分在python3
“內置類型是指任何語言在設計初期定義的類型,如c語言中的int, double, char... 它也是在一種語言中最基本的類型,?
與編譯器編譯出的代碼具有重大關系.
"新式類"和“經典類”的區分在python3中就已經不存在了,而在之后的版本里,所有的類都派生出了自類置類型object,即所有的類都是新式類
經典類繼承算法是深度優先,新式類則是廣度優先
class? A():#此代碼在2.7.14中運行,也可以在3.6.3中運行,但是許多大型的經典類實例,只能在2.7中運行
? ? var='classic class A'
class B(A):
? ? pass
class C():
? ? var='class C'
class sunclass(B,C):
? ? pass
if __name__=='__main__':
? ? print(sunclass.var)
最后結果:classic class A
再來看新式類
class ?A(object):
? ?var='classic class A'
class B(A):
? ?pass
class C(A):
? ?var='class C'
class sunclass(B,C):
? ?pass
if __name__=='__main__':
? ?print(sunclass.var)
最后結果:class C
這恰好就是上節分享的深度優先和廣度優先
但是值得注意的是,新式類并非全部都是廣度優先,而是采用c3算法(這一部分以后繼續進行介紹)
可是為什么python3之中,會出現新式類呢,經典類用的不是挺好的嗎,這個需要通過具體問題來進行分析
在經典類中所有的類都是classobj類型,而類的實例是instance類型,類與實例只有通過__class__屬性進行關聯,這樣在判斷實例時,就會造成不便,所有的類都是instance類型
class A():pass#此代碼在python32.7中運行
class B():pass
a = A()
b = B()
if __name__ == '__main__':
? ?print(type(a))
? ?print(type(b))
? ?print(type(a) == type(b))
最后結果
<type 'instance'>
<type 'instance'>
True
type(a) == type(b)的結果永遠為True,那這樣的比較就毫無意義。
更為麻煩的是,經典類的實例是instance類型,而內置類的實例卻不是,無法統一。
但是在python3中卻沒有這樣的要求
同樣代碼,在python3中的運行結果
<class '__main__.A'>
<class '__main__.B'>
False
綜上:
Python3中所有的類都是新式類,新式類中類與類型已經統一:類實例的類型是這個實例所創建自的類(通常是和類實例的__class__相同),而不再是Python 2.x版本中的“instance”實例類型。
五星級:字段,屬性(重要)
字段分為,靜態字段和普通字段
靜態字段:屬于類
普通字段:屬于對象,
讓我們來通過一個小代碼,了解什么是靜態字段,和普通字段
class Provinces:
? ?#靜態字段
? ?country='中國'
? ?def __init__(self,name):
? ? ? ?self.name=name#普通字段
#直接訪問普通字段
obj=Provinces('河北省')
print(obj.name)
#直接訪問靜態字段
print(Provinces.country)
靜態字段在內存中只保存一份
普通字段在每個對象中都要保存一份
屬性:
屬性有兩個知識點:
1,屬性的基本使用
2,屬性的兩種定義方式
一:屬性的基本使用:
class Foo:
? ?def func(self):
? ? ? ?print('求魔')
? ?#定義屬性
? ?@property#定義屬性之前的必須操作
? ?def pro(self):
? ? ? ?return "wu pei qi"
foo_obj=Foo()
foo_obj.func()
c=foo_obj.pro
print(c)
屬性的定義和調用注意以下幾點:
1:定義時,在普通方法的基礎上調用@property方法
2:定義時,屬性中僅有一個self函數
3:調用時無需括號
? ? ? ? ? ? ? ??foo_obj.func()
? ? ? ? ? ? ? ? c=foo_obj.pro
屬性存在意義是:訪問屬性時可以制造出和訪問字段完全相同的假象
屬性由方法變種而來,如果Python中沒有屬性,方法完全可以代替其功能。
實例:對于主機列表頁面,每次請求不可能把數據庫中的所有內容都顯示到頁面上,而是通過分頁的功能局部顯示,所以在向數據庫中請求數據時就要顯示的指定獲取從第m條到第n條的所有數據(即:limit m,n),這個分頁的功能包括:
根據用戶請求的當前頁和總數據條數計算出 m 和 n
根據m 和 n 去數據庫中請求數據?
class Pager:
? ? def __init__(self,current_page):
? ? ? ? #用戶當前請求的頁碼(第一頁,第二頁...)
? ? ? ? self.current_page=current_page
? ? ? ? #每頁默認顯示10條數據
? ? ? ? self.per_items=10
? ? @property
? ? def start(self):
? ? ? ? val=(self.current_page-1)*self.per_items
? ? ? ? return val
? ? @property
? ? def end(self):
? ? ? ? val=self.current_page*self.per_items
? ? ? ? return val
p = Pager(1)
print(p.start)#就是起始值,即:m
print(p.end) #就是結束值,即:n
屬性的兩種定義方式
1,裝飾器 即:在方法上運用裝飾器
2,靜態字段 ,即在類中定義值為property對象的靜態字段
第一種裝飾器方式:(在類的普通方法上應用@property裝飾器)
(此處只介紹新式類的方法)
#新式類:
class Goode(object):
? ? @property
? ? def price(self):
? ? ? ? print('@property')
? ? @price.setter
? ? def price(self, value):
? ? ? ? print('@price.setter')
? ? @price.deleter
? ? def price(self):
? ? ? ? print('@price deleter')
obj=Goode()
obj.price
obj.price=123
del obj.price? ? ? # 自動執行 @price.deleter 修飾的 price 方法
新式類中的屬性有三種訪問方式,并分別對應了三個被@property、@方法名.setter、@方法名.deleter修飾的方法
由于新式類中具有三種訪問方式,我們可以根據他們幾個屬性的訪問特點,分別將三個方法定義為對同一個屬性:獲取、修改、刪除
class Goode(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
? ? ? ? print('%s'%(value))
? ? @price.deleter
? ? def price(self,value):
? ? ? ? del self.original_price
obj=Goode()
print(obj.price)
obj.price=81
靜態字段方式,創建值為property對象的靜態字段
class Foo:
? ? def get_bar(self):
? ? ? ? return 'wupeiqi'
? ? BAR=property(get_bar)
obj=Foo()
result=obj.BAR
print(result)
這是一個小實例
我們再來看一個蠻經典的實例
class Foo(object):
? ? def __init__(self,name):
? ? ? ? self.name=name
? ? def get_bar(self):
? ? ? ? return self.name
? ? def set_bar(self,value):
? ? ? ? self.name=value
? ? def del_bar(self):
? ? ? ? del self.name
? ? BAR=property(get_bar,set_bar,del_bar,'desadfas')
obj = Foo('100')
print(obj.BAR)? # 自動調用第一個參數中定義的方法:get_bar
obj.BAR = "alex"#print(obj.)# 自動調用第二個參數中定義的方法:set_bar方法,并將“alex”當作參數傳入
print(obj.BAR)
del Foo.BAR? # 自動調用第三個參數中定義的方法:del_bar方法,進行刪除
class Goods(object):
? ? def __init__(self):
? ? ? ? self.origina_price=100
? ? ? ? self.discount=0.8
? ? def get_price(self):
? ? ? ? new_price=self.origina_price*self.discount
? ? ? ? return new_price
? ? def set_price(self,value):
? ? ? ? self.original_price = value
? ? def del_price(self):
? ? ? ? del self.original_price
? ? PRICE = property(get_price, set_price, del_price, '價格屬性描述...')
obj = Goods()
obj.PRICE? ? ? ? ?# 獲取商品價格
obj.PRICE = 200? ?# 修改商品原價
del obj.PRICE? ? ?# 刪除商品原價
第三部分:靜態方法、類方法、屬性方法
1,靜態方法
簡單理解:通過@staticmethod裝飾器即可把其裝飾的一個方法轉變為靜態方法,但什么是靜態方法,普通方法必須實例化之后才能調用,但是靜態方法,已經跟類本身沒有關系,它與類的為唯一關聯是需要通過類名才能調用這個方法
class Dog(object):
? ? def __init__(self,name):
? ? ? ? self.name=name
? ? @staticmethod
? ? def eat(self):
? ? ? ? print("%s is eating"%self.name)
d=Dog("dsfas")
d.eat()
上述會出現一系列錯誤
因為當eat變成靜態方法后,再通過實例調用時就不會自動把實例本身當作一個參數傳給self了。
而要解決它的方式有兩種
一個是把d當成self傳入當中
另一個就是把self去掉
class Dog(object):
? ? def __init__(self,name):
? ? ? ? self.name = name
? ? @staticmethod
? ? def eat():
? ? ? ? print(" is eating")
d = Dog("ChenRonghua")
d.eat()
即可
2,類方法
類方法通過@classmethod裝飾器實現,類方法和普通方法的區別是, 類方法只能訪問類變量,不能訪問實例變量
class Dog(object):? ?def __init__(self,name):
? ? ? ?self.name=name
? ?@classmethod
? ?def eat(self):
? ? ? ?print("%s is eating" %self.name)
d=Dog("chenronghua")
d.eat()
這段代碼,執行時會進行報錯
所以,我們需要定義一個類變量
class Dog(object):? ?name = "我是類變量"
? ?def __init__(self, name):
? ? ? ?self.name = name
? ?@classmethod
? ?def eat(self):
? ? ? ?print("%s is eating" % self.name)
d = Dog("ChenRonghua")
d.eat()
3,屬性方法
剛才我們提到了屬性變量,其實屬性變量就是屬性方法
這次我們來做一個小案例
比如 ,你想知道一個航班當前的狀態,是到達了、延遲了、取消了、還是已經飛走了, 想知道這種狀態你必須經歷以下幾步:
1. 連接航空公司API查詢
2. 對查詢結果進行解析?
3. 返回結果給你的用戶
lass Flight(object):? ?def __init__(self,name):
? ? ? ?self.flight_name=name
? ?def checking_status(self):
? ? ? ?#print('檢查飛機的運行狀態:'%self.flight_name)
? ? ? ?return 1
? ?@property
? ?def flight_status(self):
? ? ? ?status=self.checking_status()
? ? ? ?if status==0:
? ? ? ? ? ?print('飛機取消了')
? ? ? ?elif status==1:
? ? ? ? ? ?print('飛機飛行之中')
? ? ? ?elif status==2:
? ? ? ? ? ?print('飛機降落了')
? ? ? ?else:
? ? ? ? ? ?print("不能夠證明飛機的運行狀態")
? ?@flight_status.setter
? ?def flight_status(self,status):
? ? ? ?status_dic={
? ? ? ? ? ?0:"canceled",
? ? ? ? ? ?1:"arrived",
? ? ? ? ? ?2:"depatured"
? ? ? ?}
? ? ? ?print("\033[31;1m已經修改了飛行的狀態到 \033[0m", status_dic.get(status))
? ?@flight_status.deleter
? ?def flight_status(self):
? ? ? ?print("status got removed")
f=Flight("波音777")
f.flight_status
f.flight_status=2
好了,非常基礎的東西就講完了,我們來講一點比較偏門的,畢竟學習一門語言還要了解他的一些源代碼,知道是怎樣運行的,才能不斷精進
類的特殊成員用法:
1,__doc__:表示類的描述信息:
? ? ?在開發中,我們需要在代碼中多加注釋,來增強代碼的可讀性,用__doc__就可以查看函數或類的描述信息
def test():? ?'''
? ?this is a test function
? ?'''
? ?pass
class ClassTest(object):
? ?'''
? ?this is a class method
? ?'''
print(test.__doc__)
print(ClassTest.__doc__)
這個函數可以用來查看注釋
2、__module__和__class__
__module__ 表示對象所在的模塊
__class__ ?表示對象所在的類是什么
3,__init__ 構造方法,通過類創建對象時,自動觸發執行
5. __call__?對象后面加括號,觸發執行。
注:構造方法的執行是由創建對象觸發的,即:對象 = 類名() ;而對于 __call__ 方法的執行是由對象后加括號觸發的,即:對象() 或者 類()()
class Foo:? ?def __init__(self):
? ? ? ?pass
? ?def __call__(self, *args, **kwargs):
? ? ? ?print('call')
obj=Foo()
obj()
6.__dict__ 查看類或對象中的所有成員?
? ?country='China'
? ?def __init__(self,name,count):
? ? ? ?self.name=name
? ? ? ?self.count=count
? ?def func(self,*args,**kwargs):
? ? ? ?print('func')
print(Province.__dict__)#獲取類的成員,靜態字段,方法
obj1=Province('HeBei',10000)
print(obj1.__dict__)#獲取obj1的成員
7.__str__?如果一個類中定義了__str__方法,那么在打印 對象 時,默認輸出該方法的返回值
class
?Foo:
????
def
?__str__(
self
):
????????
return
?'alex li'
obj?
=
?Foo()
print
?(obj)
# 輸出:alex li
8.__getitem__、__setitem__、__delitem__
用于索引操作,如字典。以上分別表示獲取、設置、刪除數據
class Foo(object):? ?def __getitem__(self, item):
? ? ? ?print('__getitem',item)
? ?def __setitem__(self, key, value):
? ? ? ?print('__setitem__',key,value)
? ?def __delitem__(self, key):
? ? ? ?print('__delitem__',key)
obj=Foo()
result=obj['k1']
obj['k2']='alex'
del obj
最后一部分:
反射即想到4個內置函數分別為:getattr、hasattr、setattr、delattr ?獲取成員、檢查成員、設置成員、刪除成員現對這幾個函數進行細致的分享,
getattr(object, name[,default])
獲取對象object的屬性或者方法,如果存在即打印出來,如果不存在,即打印出默認值,默認值可選
需要注意的是,如果返回的是對象的方法,返回的是方法的內存地址,如果運行這個方法,可以在后面添加一對括號
class test():? ?name="xiaohua"
? ?def run(self):
? ? ? ?print( "helloworld")
t=test()
print(getattr(t,"name"))#獲取name屬性,存在就打印出來。
print(getattr(t, "run")) ?#獲取run方法,存在就打印出方法的內存地址。
getattr(t, "run")() ?#獲取run方法,后面加括號可以將這個方法運行。
print(getattr(t, "age","18")) ?#若屬性不存在,返回一個默認值。
hasattr(object, name)判斷一個對象里面是否有name屬性或者name方法,返回BOOL值,有name特性返回True, 否則返回False。需要注意的是name要用括號括起來
class test():? ?name="xiaohua"
? ?def run(self):
? ? ? ?print( "helloworld")
t=test()
print(hasattr(t,"name"))#判斷對象有name屬性
print(hasattr(t, "run")) ?#判斷對象有run方法
setattr(object, name, values)
給對象的屬性賦值,若屬性不存在,先創建再賦值
class test():? ?name="xiaohua"
? ?def run(self):
? ? ? ?print( "helloworld")
t=test()
print(hasattr(t,"age"))#判斷屬性是否存在
setattr(t, "age", "18") ? #為屬相賦值,并沒有返回值
print(hasattr(t, "age") ) ? #屬性存在了
綜合運用
class Foo(object):
? ? def __init__(self):
? ? ? ? self.name = 'wupeiqi'
? ? def func(self):
? ? ? ? return 'func'
obj = Foo()
#### 檢查是否含有成員 ####
print(hasattr(obj, 'name'))
print(hasattr(obj, 'func'))
#### 獲取成員 ####
getattr(obj, 'name')
getattr(obj, 'func')
#### 設置成員 ####
setattr(obj, 'age', 18)
setattr(obj, 'show', lambda num: num + 1)
#### 刪除成員 ####
delattr(obj, 'name')
#delattr(obj, 'func')#不能用于刪除方法,否則報錯
轉載于:https://blog.51cto.com/13348847/1981001