面向對象進階


isinstance(obj,cls)和issubclass(sub,super)

 isintance(obj,class)檢查obj是否是類class的對象

class Foo:
pass

f1
=Foo()
print(isinstance(f1,Foo))

 

還可以這么玩

print(isinstance([],list))
print(isinstance((),tuple))
print(isinstance('',str))
print(isinstance({},dict))

issubclass(sub,super)檢查sub類是否是super類的派生類

class Foo:
pass

class Bar(Foo):
pass

print(issubclass(Bar,Foo))

 

 

反射

什么是反射

反射的概念是由Smith在1982年首次提出的,主要是指程序可以訪問、檢測和修改它本身狀態或行為的一種能力(自省)。這一概念的提出很快引發了計算機科學領域關於應用反射性的研究。它首先被程序語言的設計領域所采用,並在Lisp和面向對象方面取得了成績。

 

python面向對象中的反射:通過字符串的形式操作對象相關的屬性。python中的一切事物都是對象(都可以使用反射)

四個可以實現自省的函數

下列方法適用於類和對象(一切皆對象,類本身也是一個對象)

 

#例子
class
Chinese: country='China'
def __init__(self,name,age):
self.name
=name
self.age
=age

p1
=Chinese('jack','18')
#hasattr
print(hasattr(p1,'x'))
#判斷p1對象中有沒有'x'字符串
hasattr
#getattr
print(getattr(p1,'name'))
#判斷p1對象中有沒有name字符串

print(getattr(p1,'x','找不到'))
#getattr可以定義三個參數,最后一個參數為找不到時返回信息
getattr
# setattr
print(p1.__dict__) #沒改之前查看一下
setattr(p1,'age','100') #設置值不需要打印
print(p1.__dict__) #改過之后查看一下
print(getattr(p1,'age'))
setattr
# delattr
print(p1.__dict__)
delattr(p1,
'age') #刪除值
print(p1.__dict__)
delattr

 

可插拔式編程

比如說要寫一個ftp功能

#類的定義者
class FtpServer:
def __init__(self,ip):
self.ip
=ip
# def conn(self):
# print('正在連接%s....' %self.ip)
#編寫到這里,定義者突然間有急事,三個月內無法繼續編寫
#客戶端實現者
from FTP_SERVER import FtpServer

obj
=FtpServer('1.1.1.1')
#對於客戶端實現者來說,要跟着服務端的腳步,即服務端實現的功能我客戶端才能調用,但如果服務端實現者臨時有事未來三個月內無法實現編寫怎么處理

if hasattr(obj,'conn'):
func
=getattr(obj,'conn')
func()
#進行判斷處理,通過反射,查看當前類中的conn方法是否實現,實現加括號就進行調用,
else:
print('編寫其它代碼邏輯')

 

即使后來類的定義者回來了,后續的功能都寫好了,但是對於客戶端實現者來說,不用再回去把前面的代碼進行修改

#如果類的定義者一行代碼都沒有寫,但我還要繼續寫下去,怎么處理。
import FTP_SERVER
if hasattr(FTP_SERVER,'FtpServer'):
cls_fs
=getattr(FTP_SERVER,'FtpServer')
print(cls_fs)
#進行判斷,查看這個文件下面是否有這個類,在python中一切接對象么。所以文件也是一個對象
#
如果找不到就進行編寫其他代碼邏輯
print('其他邏輯')

 

 __setattr__,__delattr__,__getattr__

class Foo:
x
=1
def __init__(self,y):
self.y
=y

def __getattr__(self, item):
print('----> from getattr:你找的屬性不存在')

def __setattr__(self, key, value):
print('----> from setattr',key,value)
#只給實例去用,類無法觸發
self.__dict__[key]=value

def __delattr__(self, item):
print('----> from delattr')
self.
__dict__.pop(item)
# print(item)

f
=Foo(100)
# print(f.x)
#
print(f.y)
#
能找到的屬性並不會除非__getattr__

# print(f.z)
#
f.z
#
查找一個找不到的屬性就會觸發__getattr__

# del f.y
#
注釋掉__delattr__ 'self.__dict__.pop(item)' 執行del f.y可以觸發__delattr__


#設置值
f.z=1
# print(f.z)
#
並沒有真正設定值。在__setattr__中 添加代碼'self.__dict__[key]=value'
#
f.z=1的意思是在f的名稱空間中加入一個z 值為1。在名稱空間中都是那種key:value格式所有我們可以用上面的方法將值加入到f的名稱空間中

# 查看值
#
print(getattr(f,'z'))
#
print(f.__dict__)


# 刪除值
print(f.__dict__) #刪除前查看一次
del f.z
print(f.__dict__) #刪除后查看一次,

#上面打印的item結果就是要刪除的z,有了key我們就可以刪除,代碼'self.__dict__.pop(item)'

 

二次加工標准類型(包裝)

#自定義列表
class List(list): #繼承python內置list,
pass

# l=List([1,2,3])
#
print(l)
#
沒毛病,直接實例化調用內置list的init方法
# 進行二次加工,添加append功能,並限制
class List(list):
def append(self, p_object): #self是列表本身,p_object為要添加的值
if not isinstance(p_object,int):
raise TypeError('Must be int type') #定義規則,只能添加數字類型
super().append(p_object) #使用super調用父類,再次查看

l
=List([1,2,3,4])
l.append(
'asdasd')
print(l)

 

授權

授權是包裝的一個特性, 包裝一個類型通常是對已存在的類型的一些定制,這種做法可以新建,修改或刪除原有產品的功能。其它的則保持原樣。授權的過程,即是所有更新的功能都是由新類的某部分來處理,但已存在的功能就授權給對象的默認屬性。

使用的方法:__getattr__

#自定義Open方法,實現每一行開頭都顯示時間
#
class Open:
#
def __init__(self,file_path,mode='r'):
#
self.obj=open(file_path,mode)
#
#
f=Open('a.txt','w')
#
f.obj.write('1111111111111') #open打開文件寫的操作就是f.write,而不是f.obj.write
#
上面的方法實現了自定義Open的方法,但是沒有時間功能

import time
class Open:
def __init__(self,file_path,mode='r'):
self.obj
=open(file_path,mode)

def write(self,file): #定義一個write方法,讓f.obj.write 變成f.write
file='%s %s \n' %(time.strftime("%Y-%m-%d %X"),file) #然后在真正的寫操作之前進行一個字符串拼接
self.obj.write(file) #f這個對象傳到self,file參數接受要輸入的內容


f
=Open('a.txt','w')
f.write(
'11111111111111111111111111111111\n')

上面的方法已經可以實現這個功能,但是想要f.close()呢?怎么辦?

import time
class Open:
def __init__(self,file_path,mode='r'):
self.obj
=open(file_path,mode)

def write(self,file):
file
='%s %s \n' %(time.strftime("%Y-%m-%d %X"),file)
self.obj.write(file)

def __getattr__(self, item): #定義一個getattr函數
# print('--------------->',item)
return getattr(self.obj,item) #調用真正的close並返回


f
=Open('a.txt','w')
f.close()
#我想要關閉文件f。但是close不屬於f,而屬於self.obj,那我們就拿到他

#首先f.close會在f的類中找close,找不到就會觸發__getattr__的運行
#
那么close的字符串會傳給item這個參數,然后通過getattr去真正的對象里面去找有沒有close這個名字
#
找到了那么就是一個內存地址並返回,那么f.close()拿到的就是f.obj.close()

 

__setitem__,__getitem,__delitem__

就是在把對象模擬成字典的形式,就可以用key值取value值

class Foo:
def __init__(self,name):
self.name
=name

def __getitem__(self, item): #真正的字典取值
# print(self.__dict__[item]) #使用print方法執行 print(f['name'])會打印,但是也會返回None,所以用ret直接返回
return 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): #而delattr與delitem區別是delattr是通過obj.key,delitem是obj['key']
print('del obj.key時,我執行')
self.
__dict__.pop(item)

f
=Foo('jack')
# print(f.name)
print(f['name']) #通過getitem找值
f['age']='25' #通過setitem設置值
f['sex']='man'

# print(f.__dict__) #再次查看
del f.name #通過delattr刪除
del f['age'] #通過delitem刪除
print(f.__dict__)
e,g

 

__slots__

class Foo:
__slots__=['x','y'] #限制類中,只能有x,y參數
def __init__(self,x,y,z):
self.x
=x
self.y
=y
self.z
=z

f
=Foo(1,2,3)
#實例化報錯,提示Foo沒有z這個參數
1.__slots__是什么:是一個類變量,變量值可以是列表,元祖,或者可迭代對象,也可以是一個字符串(意味着所有實例只有一個數據屬性)
2.引子:使用點來訪問屬性本質就是在訪問類或者對象的__dict__屬性字典(類的字典是共享的,而每個實例的是獨立的)
3.為何使用__slots__:字典會占用大量內存,如果你有一個屬性很少的類,但是有很多實例,為了節省內存可以使用__slots__取代實例的__dict__當你定義__slots__后,__slots__就會為實例使用一種更加緊湊的內部表示。實例通過一個很小的固定大小的數組來構建,而不是為每個實例定義一個字典,這跟元組或列表很類似。在__slots__中列出的屬性名在內部被映射到這個數組的指定小標上。使用__slots__一個不好的地方就是我們不能再給實例添加新的屬性了,只能使用在__slots__中定義的那些屬性名。


4.注意事項:__slots__的很多特性都依賴於普通的基於字典的實現。另外,定義了__slots__后的類不再 支持一些普通類特性了,比如多繼承。大多數情況下,你應該只在那些經常被使用到 的用作數據結構的類上定義__slots__比如在程序中需要創建某個類的幾百萬個實例對象 。
關於__slots__的一個常見誤區是它可以作為一個封裝工具來防止用戶給實例增加新的屬性。盡管使用__slots__可以達到這樣的目的,但是這個並不是它的初衷。 更多的是用來作為一個內存優化工具。
__slots__使用
class Foo:
__slots__=['x','y']
# def __init__(self,x,y,z):
# self.x=x
# self.y=y
# self.z=z

f
=Foo()
f1
=Foo()
#正常來說,實例化后實例會有一個自己的名稱空間,也就是__dict__查看,那么使用了__slost__就統一共用一個名稱空間有slost提供
#
查看實例的名稱空間
print(f.__dict__)
print(f1.__dict__)

 

 __next__和__iter__實現迭代器協議

#自定義迭代器
class Foo:
def __iter__(self):
pass
def __next__(self):
pass

f
=Foo()

from collections import Iterator,Iterable
print(isinstance(f,Iterable))
print(isinstance(f,Iterator))
#實現迭代器range功能

class Foo:
def __init__(self,start,stop):
self.start
=start
self.stop
=stop

def __iter__(self):
return self

def __next__(self):
if self.start == self.stop: #進行判斷,如果不判斷,print(f.__next__()) 會無限下去,判斷成功,拋出異常
raise StopIteration
res
=self.start #將開始值暫存,並返回
self.start+=1 #將開始值+1,比如說第一次是1 暫存到res中,返回,之后執行第一次值+1
return res

f
=Foo(1,5) #實例化
print(f.__next__()) #取值
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())

for i in f:
print(i)
實現迭代器range功能

 

__del__

析構方法,當對象在內存中被釋放時,自動觸發執行。

注:此方法一般無須定義,因為Python是一門高級語言,程序員在使用時無需關心內存的分配和釋放,因為此工作都是交給Python解釋器來執行,所以,析構函數的調用是由解釋器在進行垃圾回收時自動觸發執行的。

class Foo:
def __del__(self): #__del__叫析構方法,在del刪除對象的時候會執行
print('---->') #通常在里面寫一些清理操作

f
=Foo()
# del f

 

__enter__和__exit__

上述叫做上下文管理協議,即with語句,為了讓一個對象兼容with語句,必須在這個對象的類中聲明__enter__和__exit__方法

class Open:
def __init__(self,name):
self.name
=name

def __enter__(self):
# print(self)
print('出現with語句,對象的__enter__被觸發,有返回值則賦值給as聲明的變量')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('with中代碼塊執行完畢時執行')
print('exe_type',exc_type) #異常類型
print('exc_val',exc_val) #異常值
print('exc_tb',exc_tb) #異常追蹤
#拋出異常后會有結果,只要一拋異常,程序就自動退出,后面代碼將不會執行
# return True
# 如果exit返回值為True異常將不會拋出,只會被exit接收到

with Open(
'a.txt') as f:
print('=====>執行代碼塊',f)
print(dasdasdasd)
print('==========================================================>')
# print(f,f.name)

 

__call__

class Foo:
def __call__(self, *args, **kwargs):
print('-----L>')

f1
=Foo()
f1()
#只要在類中定義了__call__那么對象就可以加括號運行,暫時用不到

 

未完待續...

 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



面向對象進階 面向對象:進階 面向對象進階 面向對象進階 面向對象進階(一) 面向對象進階 面向對象進階(二) 面向對象進階 面向對象進階 面向對象進階
 
粤ICP备14056181号  © 2014-2021 ITdaan.com