【python3學習筆記】 ---- python之反射機制詳解、getattr、hasattr、setattr、delattr函數詳解

        在程序開發中,常常會遇到這樣的需求:在執行對象中的某個方法,或者在調用對象的某個變量,但是由於一些原因,我們無法確定或者並不知道該方法或者變量是否存在,這時我們需要一個特殊的方法或者機制來訪問或操作該未知的方法或變量,這種機制就被稱之爲反射

    · 反射機制:反射就是通過字符串的形式,導入模塊;通過字符串的形式,去模塊中尋找指定函數,對其進行操作。也就是利用字符串的形式去對象(模塊)中操作(查找or獲取or刪除or添加)成員,一種基於字符串的事件驅動。

下面先介紹發射機制中的常用的幾個函數:

· getattr()  函數用於返回一個對象的屬性值

獲取對象object的屬性或者方法,存在則返回其屬性,不存在則返回默認值,默認值可選。

注意:如果獲取的是方法,存在則返回對象中方法的內存地址,若想運行則需通過"()"方法.

# getattr 語法
getattr(object,name[,default])

#參數
# · object -- 對象
# · name   -- 字符串,對象屬性
# · default-- 默認返回值,如果不提供該參數,在沒有對應屬性時,將觸發AttrbuteError.

# 返回值 :返回對象屬性值 
class test():
    name="david"
    def run(self):
        return "Hello David"
t=test()        # t 爲一個test對象
getattr(t, "name") #獲取name屬性
getattr(t, "run")  #獲取run方法,存在就打印出方法的內存地址。
<bound method test.run of <__main__.test instance at 0x0269C878>>
getattr(t, "run")()  #獲取run方法,後面加括號可以將這個方法運行。
'Hello David'
getattr(t, "david")  #獲取一個不存在的屬性。
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: test instance has no attribute 'david'
getattr(t, "david","18")  #若屬性不存在,返回一個默認值。
'18'

· hasattr() 函數用於判斷對象是夠包含對應的屬性

判斷一個對象裏面是否有name屬性或者name方法,返回BOOL值,有name特性返回True, 否則返回False。

需要注意的是name是一個字符串字面值或字符串類型變量。

# hasattr 語法
hasattr(object,name)

#參數
# · object -- 對象
# · name   -- 字符串,屬性名

# 返回值 :如果對象有該屬性返回 True,否則返回 False。
t=test()           # test 還是上面的那個
hasattr(t, "name") #判斷對象有name屬性
True
hasattr(t, "run")  #判斷對象有run方法
True

· setattr() 函數對應函數getattr(),用於設置屬性值,若屬性不存在,則先創建在賦值。

給對象的屬性賦值,若屬性不存在,先創建再賦值。

# setattr 語法
setattr(object, name, value)

#參數
# · object -- 對象
# · name   -- 字符串,屬性名
# · value  -- 屬性值。

# 返回值 :無
>>> t=test()
>>> hasattr(t, "hdw")   #判斷屬性是否存在
False
>>> setattr(t, "hdw", "18")   #爲屬相賦值,並沒有返回值
>>> hasattr(t, "hdw")    #屬性存在了
True

· delattr() 函數用來刪除指定對象的指定名稱的屬性,和setattr函數作用相反,屬性必須存在,否則發出AttributeError。

delattr(object, name)
  This is a relative of setattr(). The arguments are an object and a string. The string must be the name of one of the object’s attributes. The function deletes the named attribute, provided the object allows it. For example, delattr(x, 'foobar') is equivalent to del x.foobar.
#定義類A
class A:
    def __init__(self,name):
        self.name = name
    def sayHello(self):
        print('hello',self.name)
A a;    # a是一個A對象
#測試屬性和方法
a.name
'小麥'
a.sayHello()
hello 小麥

#刪除屬性
>>> delattr(a,'name')
>>> a.name
Traceback (most recent call last):
  File "<pyshell#47>", line 1, in <module>
    a.name
AttributeError: 'A' object has no attribute 'name'
>>> a.name #屬性name已經刪掉,不存在
Traceback (most recent call last):
  File "<pyshell#47>", line 1, in <module>
    a.name
AttributeError: 'A' object has no attribute 'name'

>>> delattr(a,'name') #再刪除會報錯
Traceback (most recent call last):
  File "<pyshell#48>", line 1, in <module>
    delattr(a,'name')
AttributeError: name
>>> a.sayHello
<bound method A.sayHello of <__main__.A object at 0x03F014B0>>
>>> delattr(a,'sayHello') #不能用於刪除方法
Traceback (most recent call last):
  File "<pyshell#50>", line 1, in <module>
    delattr(a,'sayHello')
AttributeError: sayHello
>>>

· eval() 函數用來執行一個字符串表達式,並返回表達式的值。

# 語法
eval(expression[, globals[, locals]])

#參數
# expression -- 表達式。
# globals 	 -- 變量作用域,全局命名空間,如果被提供,則必須是一個字典對象。
# locals	 -- 變量作用域,局部命名空間,如果被提供,可以是任何映射對象。

# 返回值:表達式計算結果。
>>>x = 7
>>> eval( '3 * x' )
21
>>> eval('pow(2,2)')
4

反射機制的用法:    

imp = input(“請輸入你想導入的模塊名:”)
CC = __import__(imp) 這種方式就是通過輸入字符串導入你所想導入的模塊 
CC.f1()  # 執行模塊中的f1方法

上面實現了動態輸入模塊名,從而可以執行裏面的函數,但是有一個缺點,就是執行的函數被固定了。那如何動態輸入函數名並且來執行呢?這就的通過反射機制。

#dynamic.py
imp = input("請輸入模塊:")
dd = __import__(imp)    # 等價於import imp
inp_func = input("請輸入要執行的函數:")

f = getattr(dd,inp_func,None)    #作用:從導入模塊中找到你需要調用的函數inp_func,然後返回一個該函數的引用.沒有找到就煩會None

f() # 執行該函數
上面實現了,動態導入一個模塊,並且動態輸入函數名然後執行相應功能。

當然,上面還存在一點點小問題:那就是我的模塊名有可能不是在本級目錄中存放着。有可能是如下圖存放方式:

  

那麼就得用下面的方式了。

dd = __import__("lib.text.commons")  #這樣僅僅導入了lib模塊
dd = __import__("lib.text.commons",fromlist = True)  #改用這種方式就能導入成功
# 等價於import config
inp_func = input("請輸入要執行的函數:")
f = getattr(dd,inp_func)
f()

平常可能需要用到上面的四個函數。

r = hasattr(commons,xxx)             # 判斷某個函數或者變量是否存在
print(r)  

setattr(commons,'age',18)            # 給commons模塊增加一個全局變量age = 18,創建成功返回none

setattr(config,'age',lambda  a:a+1)  # 給模塊添加一個函數

delattr(commons,'age')               # 刪除模塊中某個變量或者函數
注:getattr,hasattr,setattr,delattr對模塊的修改都在內存中進行,並不會影響文件中真實內容。

反射機制常常都是使用在web框架上,比如你瀏覽某個網頁,你點網頁上的文字或則圖片,則會跳轉或者說生成新的頁面,這是怎麼實現的呢?就是採用反射機制實現的,當你點擊某個東西是不是就對應不同的url,而url是字符串的形式,穿進去,就可以通過那幾個函數找到對應的實現方法。

下面來一個基於反射機制模擬的web框架路由

需求:輸入www.xxx.com/commons/fun,則返回fun的結果,不存在則返回404

# 動態導入模塊,並執行其中函數
url = input("url: ")

target_module, target_func = url.split('/')
m = __import__('lib.'+target_module, fromlist=True)

inp = url.split("/")[-1]  # 分割url,並取出url最後一個字符串
if hasattr(m,target_func):  # 判斷在commons模塊中是否存在inp這個字符串
    target_func = getattr(m,target_func)  # 獲取inp的引用
    target_func()  # 執行
else:
    print("404")





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