Python反射,讓你看懂得別人開發的代碼

Python說:一切事物都是對象

什麼是反射: 在計算機科學領域,反射是指一類應用,它們能夠自描述和自控制。也就是說,這類應用通過採用某種機制來實現對自己行爲的描述(self-representation)和監測(examination),並能根據自身行爲的狀態和結果,調整或修改應用所描述行爲的狀態和相關的語義

python面向對象中的反射: 通過字符串的形式操作對象相關的屬性,python中的一切事物都是對象(都可以使用反射),利用字符串的形式去對象(模塊)中操作(查找/獲取/刪除/添加)成員(自省),一種基於字符串的事件驅動。通俗來講就是反射就像我們日常照鏡子一樣,通過鏡子的反射,我們可以進行整理衣服,或者將原來的衣服去除。這個過程就稱之爲反射。

python通過四大內置函數來實現反射功能

getattr

getattr(object, name[, default])

返回對象命名屬性的值。name 必須是字符串。如果該字符串是對象的屬性之一,則返回該屬性的值。例如, getattr(x, ‘foobar’) 等同於 x.foobar。如果指定的屬性不存在,且提供了 default 值,則返回它,否則觸發 AttributeError。

獲取對象中的方法或者變量的地址:

class man:
    def mirror(self):
        print('hello python')

m = man()
ret = getattr(m,'mirror')
ret2 = getattr(m,'face','沒有臉')  添加一個默認值,如果類中沒有這個方法或者變量時,將會打印出默認值   
ret()    # m.mirror() 相當於這個方法  
pirnt(ret2)
print(ret)
print(m)
------------------------>
hello python
沒有臉
<bound method man.mirror of <__main__.man object at 0x000001C2433F6080>>
<__main__.man object at 0x000001C2433F6080>
class man:
    def mirror(self):
        print('hello python')

m = man()
m.name = 'bob'
m.age = 10
print(m.__dict__)  #查看自己的屬性字典
b = getattr(m,'age')
print(b)
-------------------------->
{'name': 'bob', 'age': 10}
10

也可以反射自己模塊中的變量和方法。
下面的代碼感興趣的可以瞭解下,__main__使用可以查看這篇
Python中所有加載到內存的模塊都放在sys.modules。當import一個模塊時首先會在這個列表中查找是否已經加載了此模塊 ,如果加載了則只是將模塊的名字加入到正在調用import的模塊的Local名字空間中。如果沒有加載則從sys.path目錄中按照模塊名稱查找模塊文件,模塊文件可以是py、pyc、pyd,找到後將模塊載入內存,並加入到sys.modules中,並將名稱導入到當前的Local名字空間。

import sys
def func():
    print('hello python')
print(sys.modules)
print(sys.modules['__main__'].func())
print(getattr(sys.modules['__main__'],'func')())
print('*'*60)
print(sys.modules[__name__])
print(sys.modules[__name__].func())
print(getattr(sys.modules[__name__],'func')())

hasattr

hasattr(object, name)

該實參是一個對象和一個字符串。如果字符串是對象的屬性之一的名稱,則返回 True,否則返回 False。(此功能是通過調用 getattr(object, name) 看是否有 AttributeError 異常來實現的。)

class man:
    def mirror(self):
        print('hello python')

m = man()
m.name = 'bob'
m.age = 10

while True:
    attr = input("請輸入你想要的查詢這個人的信息:")
    if hasattr(m,attr):
        b = getattr(m,attr)
        print(b)
    else:
        print("此人暫時只有名字(name)和年齡(age)信息")

---------------------------->
請輸入你想要的查詢這個人的信息:ab
此人暫時只有名字(name)和年齡(age)信息
請輸入你想要的查詢這個人的信息:age
10
請輸入你想要的查詢這個人的信息:name
bob
請輸入你想要的查詢這個人的信息:

setattr

setattr(object, name, value)

此函數與 getattr() 兩相對應。 其參數爲一個對象、一個字符串和一個任意值。 字符串指定一個現有屬性或者新增屬性。 函數會將值賦給該屬性,只要對象允許這種操作。 例如,setattr(x, ‘foobar’, 123) 等價於 x.foobar = 123。

這個就像當與當你自己審覈自己信息的時候發現,年齡不對,就可以進行更改:

class man:
    def mirror(self):
        print('hello python')

m = man()
m.name = 'bob'
m.age = 10

while True:
    attr = input("請輸入你想要的查詢這個人的信息:")
    if hasattr(m,attr):
        b = getattr(m,attr)
        print(b)
        age = input("如果年齡不正確,請輸入正確的年齡:")
        setattr(m,attr,age)
    else:
        print("此人暫時只有名字(name)和年齡(age)信息")
---------------------------->
請輸入你想要的查詢這個人的信息:age
10
如果年齡不正確,請輸入正確的年齡:18
請輸入你想要的查詢這個人的信息:age
18
如果年齡不正確,請輸入正確的年齡:

上面寫出了對變量可以修改,下面對方法也可以進行修改:

class man:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def mirror(self):
        print('%s hello python'%self.name)

m = man('tom','18')

setattr(m,'func',lambda self:int(self.age)+1)
print(m.__dict__)
print(m.func(m))
setattr(m,'func',lambda self:self.name + " bob")
print(m.__dict__)
print(m.func(m))   #將這個對象傳遞給lambda中的self
----------------------------->
{'name': 'tom', 'age': '18', 'func': <function <lambda> at 0x0000014B1DBCC1E0>}
19
{'name': 'tom', 'age': '18', 'func': <function <lambda> at 0x0000014B1DF03488>}
tom bob

delattr

delattr(object, name)

setattr() 相關的函數。實參是一個對象和一個字符串。該字符串 必須 是對象的某個屬性。如果對象允許,該函數將刪除指定的屬性。例如 delattr(x, ‘foobar’) 等價於 del x.foobar
如果我們給查詢這個查詢信息的人設置一個愛好,但是他不喜歡,要進行刪除操作:

class man:
    def mirror(self):
        print('hello python')

m = man()
m.name = 'bob'
m.age = 10
m.favor = 'code'

while True:
    attr = input("查詢你的愛好:")
    if hasattr(m,attr):
        print(getattr(m,attr))
        favor = input('沒有愛好,刪除個人愛好欄:')
        delattr(m,favor)
    else:
        print('確認完畢,是個變態')
 --------------------------------->
 查詢你的愛好:favor
code
沒有愛好,刪除個人愛好欄:favor
查詢你的愛好:favor
確認完畢,是個變態
查詢你的愛好:

介紹完基本的方法我們舉例加深對反射的理解:
使用反射的實例一:
導入的模塊使用反射
我們在寫python代碼的時候常常會導入模塊,會使用到 import requests 類似這樣的模塊他是怎樣進行工作的呢?
通過查詢源碼,我們可以看得 import 還可以這樣寫

def __import__(name, globals=None, locals=None, fromlist=(), level=0):

做一個簡單的實例

arg = input("你想要的的庫(requests):")
req = __import__(arg)
data = req.get("http://www.baidu.com").text
print(data)
-------------------->
你想要的的庫(requests):requests
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css>>   .*?

isinstance (obj,cls) 檢查是否obj是否是類cls的對象
issubclass(sub,super) 檢查sub類是否是super類的派生類 (子類,父類)

實例二:
如果A和B程序員同時進行開發有關FTP上傳、下載的功能,但是A生病了,爲了不影響大進度,B決定先開法下載的業務邏輯,給A留下接口編程。
A做了這些工作:

class ftpA:
    def __init__(self,addr):
        self.addr = addr

B開始開發下載功能

class ftpB:
    f = ftpA('192.168.43.2')
    def download(self,addr):
        if hasattr(f,'pull'):    #判斷f這個類中有沒有拉取的這個函數
            download = getattr(f,'pull') 
            download()           #如果有直接實現這個功能
        else:
            print('開發其他的功能')  #如果沒有先留下來,繼續開發下一個業務。

當然了,在開發工作前,B需要將來發所留下來的接口名稱給A說。

關於反射還會在補充,由於本人知識面有限,會在後續補充。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章