Python 面向對象進階

 

1 isinstance 和issubclass

instance:判斷該對象是不是類的對象

isinstance(obj,Foo) javascript

x = []
print(isinstance(x,list))

結果:
Truecss

issubclass:判斷是不是繼承

class Foo:
    pass
class Bar(Foo):
    pass
print(issubclass(Bar,Foo))

結果:
Truehtml

經過下面的方式也能夠查看
print(Bar.__bases__) java

2 反射

反射是Smi首次提出的,主要指程序能夠訪問、檢測和修改它自己狀態行爲的一種能力,也能夠叫作自省node

2.1面向對象中的反射

面向對象中的反射,是經過字符串形式操做對象的屬性,Python中一切都是對象,均可以使用對象python

Python中經過hasattr、getattr、setattr、deltattr實現自省的函數,適用於類和對象(一切都是對象)git

反射就是把字符串反射成相應的命令github

hasattr

查看類和對象中是否有name,用字符串表示,實例在找的時候首先從自身找,自身沒有從類找。web

class Foo:
    name = 'aaa'
f1 =Foo()
print(hasattr(Foo,'name'))
print(hasattr(f1,'name'))

結果:
True
Trueajax

setattr

setattr是設置屬性,setattr(x,y,z)

class Foo:
    name = 'aaa'
f1 =Foo()

setattr(Foo,'age',18)
print(Foo,'age')
print(f1,'age')

deltattr

print(Foo.__dict__)
delattr(Foo,'name')
print(Foo.__dict__)   
print(hasattr(f1,'name'))

結果:
False
經過打印類的名稱空間能夠查看到name已經沒有了

getattr

getattr是查找那個屬性,並把命名它的值得到,實際的原理是經過得到字典中的key,而後得到value

class People:
    country = 'china'
    def __init__(self,name):
        self.name=name

p = People('aaa')
print(hasattr(p,'name'))
print('name' in p.__dict__)  # 執行的效果是同樣的e

print(hasattr(p,'country'))
print(hasattr(People,'country'))

print(getattr(p,'country'))
print(getattr(p,'__init__'))
print(getattr(People,'country'))

2.3模塊的反射

Python中一切皆對象,模塊文件等都是對象

import sys

def s1():
    pass
def s2():
    pass
this_module = sys.modules[__name__]
print(hasattr(this_module,'s1'))
print(getattr(this_module,'s1'))
  • 結果:
    True

function s1 at 0x00000000005F3E18

 2.4 使用反射的好處

2.4.1 實現可拔插機制

能夠事先定義好接口,接口只有在被完成後纔會真正執行,這實現了即插即用,這實際上是一種‘後期綁定’,什麼意思?即你能夠事先把主要的邏輯寫好(只定義接口),而後後期再去實現接口的功能

 

# 第一我的負責的代碼
class FtpClient:
    def __init__(self,addr):
        print("正在鏈接服務器%s" % addr)
        self.addr = addr


# 第二我的引用的代碼
f1 = FtpClient("192.168.0.1")

if hasattr(f1, 'get'):
    func = getattr(f1, 'get')
    func()
else:
    print("不存在此方法")
    print("處理其餘邏輯")

 

第一我的的代碼只提供了接口,並無寫完,可是第二我的要用第一我的的代碼中,經過自省進行判斷,後面的人就能夠直接拿來用了

 

2.4.2 動態導入模塊

動態導入模塊(基於反射當前模塊成員)

 

3 內置 (attr ) __setattr__,__delattr__,__getattr__,

3.1__getattr__

getattr只有在調用的屬性不存在的時候纔會觸發

class Foo:
    def __init__(self,name):
        self.name = name
    def __getattr__(self, item):
        print("getattr--%s %s"%(item,type(item)))

f = Foo('aaa')
print(f.xxx)
  • 結果:
    getattr—xxx class ‘str’
    None

3.2__setattr__

setattr添加/修改屬性會觸發它的執行

class Foo:
    def __init__(self,x):
        self.name=x
    def __setattr__(self, key, value):  # 與上面的對應關係是self--self,name--key,x--value
        self.__dict__[key]=value  # 真正修改的是__dict__中的內容
f1 = Foo('aaa')
f1.x='bbb'   # 修改屬性
f1.age=18  # 增長屬性
print(f1.__dict__) # 在名稱對象的名稱空間中能夠顯示

3.3__deltattr__

只有刪除屬性的時候纔會觸發deltatr的執行

class Foo:
    def __init__(self,x):
        self.name=x
    def __setattr__(self, key, value):  # 與上面的對應關係是self--self,name--key,x--value
        self.__dict__[key]=value  # 真正修改的是__dict__中的內容
    def __delattr__(self, item):
        self.__dict__.pop(item)
f1=Foo('aaa')
print(f1.__dict__)
del f1.name
print(f1.__dict__)

結果:
{‘name’: ‘aaa’}
{}

4 反射的應用

4.1 可插拔機制

#客戶端
class FtpClient:
    def __init__(self,addr):
        print('正在鏈接服務器[%s]'%addr)
        self.addr = addr

    def get(self):
        print('get')

    def test(self):
        print('test')
#服務端,須要使用客戶端的內容
import ftpclient  # 導入test模塊(這裏是文件)
# f1 = FtpClient('192.168.1.1') # wrong
f1=ftpclient.FtpClient('192.168.1.1')   #這裏的對象是文件,即ftpclient,對這個對象進行實例化
if hasattr(f1,'get'):  # 首先看是否有這個功能
    func_get=getattr(f1,'get')  # getattr是得到相應的內存地址
    func_get()
else:  # else預語言中是真正的邏輯
    print('其餘邏輯')

4.2 經過字符串導入模塊

m = input("輸入你要導入的模塊")
m1 = __import__(m)
print(m1)
import importlib
t = importlib.import_module('time')
print(t.time)

5 二次加工標準類型(包裝)

Python內部已經有了標準的數據類型和內置方法,可是在不少的狀況下,咱們須要對數據類型進行本身定製,這幾用到了繼承和派生的知識

對append添加限制,只能添加數字類型的

class List(list): # List 繼承了列表list
    def append(self, p_object):  # 僅僅對append進行修改
        if not isinstance(p_object,int):  # 限制數據類型
            raise TypeError('must be int')
        super().append(p_object)  # 真正的修改,super是對父類進行修改
l = list([1,2,3])
print(l)
l.append(4)   # 這裏是對屬性進行修改,實際是對類進行修改
print(l)

結果:
[1, 2, 3]
[1, 2, 3, 4]

上面的狀況僅僅是對append進行了修改並添加了限制,list的其餘的屬性如insert是沒有限制的

受權

受權是包裝的一個特性,上面的狀況是經過繼承來實現的,可是函數是不能繼承的

受權的過程是全部更新的功能都是由新類的某部分來處理,已經存在的就受權給對象的默認屬性

實現受權的關鍵點就是覆蓋getattr方法

import time
class Open:
    def __init__(self,filepath,mode='r',encoding='utf-8'):
        self.x = open(filepath,mode=mode,encoding=encoding)  # self.x是文件句柄 拿到文件中的內容
        self.filepath=filepath
        self.mode=mode
        self.encoding=encoding
    def write(self,line):  # 在類中新建write功能
        t=time.strftime('%Y-%m-%d %X')
        self.x.write("%s %s" %(t,line))  # self.x存放的是文件句柄
    def __getattr__(self, item):  # 經過getattr實現的叫作受權
        # print('---->',item,type(item)) # 測試查看類型
        return getattr(self.x,item)  # 從一個文件對象得到 字符串 如今得到的是self.x 相應方法的地址
f=Open('b.txt','w') #實例化一個對象 傳入相應的參數
f.write('123\n')   #Open沒有實際的writearttribute

f.write('123\n')
f.write('123\n')
f.write('123\n')
f.write('123\n')
f.write('123\n')
f.write('123\n')

這樣就能在文件中添加了帶有時間的內容

對文件進行讀操做:

f=Open('b.txt','r')  # 從新實例化
res = f.read()# f.read實際是self.x 的方法
print(res)
f.seek(0)  #此時光標位於文件的最後
print(f.read())  #這樣就能夠從新讀文件
相關文章
相關標籤/搜索