Python的反射

昀溪發表於2018-08-18

什麼是反射

反射是一個很重要的概念,它可以把字串對映到例項的變數或者例項的方法然後可以去執行呼叫、修改等操作。它有四個重要的方法:

  • getattr 獲取指定字串名稱的物件屬性
  • setattr 為物件設定一個物件
  • hasattr 判斷物件是否有對應的物件(字串)
  • delattr 刪除指定屬性

attr是屬性英文的前幾個字母,屬性指的是類中類變數、例項變數和方法。但是要注意不能是私有的,如果你的變數是以“_”開頭,那將無法獲取。

反射常常用在動態載入模組的場景中。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: rex.cheny
# E-mail: rex.cheny@outlook.com

class TestObj(object):

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def displayName(self):
        print("displayName方法執行,列印姓名:", self.name)



def AAA(self):
    print("I am AAA.")


def main():
    to = TestObj("Tom", 23)
    # 檢視 to 例項裡面是否有 name 這個屬性
    if hasattr(to, "name"):
        print("例項 to 中有 name 屬性。")
        print(getattr(to, "name"))
    else:
        print("例項 to 中沒有 name 屬性。")

    if hasattr(to, "displayName"):
        print("例項 to 中有 displayName 屬性。")
        getattr(to, "displayName")()
    else:
        print("例項 to 中沒有 displayName 屬性。")

    if hasattr(to, "AAA"):
        print("例項 to 中有 AAA 屬性。")
        getattr(to, "AAA")()
    else:
        print("例項 to 中沒有 AAA 屬性,將會設定。")
        setattr(to, "AAA", AAA)  # 引數:例項、方法名稱、具體方法  相當於 to.AAA = AAA 第一個AAA是函式在例項中的名稱, 第二個AAA是把哪個函式放進去,兩者只是恰好這裡名稱一樣

        # to.AAA(to)  # 這裡一定要主動傳遞一個例項進去,因為它不會自動裝配self
        getattr(to, "AAA")(to)


if __name__ == '__main__':
    main()

AAA是動態裝載到例項裡面去的。

可能有些人還沒明白反射,反射就是把字串反射成記憶體物件,看下面的例子

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: rex.cheny
# E-mail: rex.cheny@outlook.com


class TestObj(object):
    def __init__(self, name):
        self.name = name


def displayname(self):
    print(self.name)


def main():
    to = TestObj(name="Tom")

    cmd = input("輸入:")
    if hasattr(to, cmd):
        pass
    else:
        setattr(to, cmd, displayname)
        func = getattr(to, cmd)
        func(to)


if __name__ == '__main__':
    main()

根據使用者輸入來呼叫函式(這個函式肯定要提前的真實存在)。我這裡無論我輸入什麼都可以執行上面的displayname方法。現在應該明白字串對映到方法了吧。

反射使用

透過字串匯入模組

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: rex.cheny
# E-mail: rex.cheny@outlook.com

temp = "re"
model = __import__(temp)


def main():
    txt = "hj123uo"
    pattern = model.compile(r"[0-9]+")
    print(model.search(pattern, txt).group())


if __name__ == '__main__':
    main()

以字串的形式使用模組的方法

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: rex.cheny
# E-mail: rex.cheny@outlook.com
 
temp = "re"  # 要引入的模組
func = "compile"  # 要使用的方法
model = __import__(temp)  # 匯入模組
function = getattr(model, func)  # 找到模組中的屬性


def main():
    txt = "hj123uo"
    pattern = function(r"[0-9]+")  # 這裡執行funcation()就等於執行re.compile()函式
    print(model.search(pattern, txt).group())


if __name__ == '__main__':
    main()

反射到底有什麼用?

上面使用re.compile()函式的整個過程看起來很麻煩,但是你要知道這就等於實現了動態載入和執行所需要的模組或方法而不需要全部寫入到PY檔案中,當然具體需要執行的方法你也要提前實現。典型的使用場景就是web的URL路由。目前所有的web框架的URL路由基本都是這個原理。
使用者輸入不同的URL如何載入不同的PY檔案以及呼叫裡面的方法呢?你想一想Django裡面,它並不是這樣的,它依然需要你設定URL以及該URL對應的PY檔案,為什麼?因為這樣除錯方便,當然你能力足夠也可以給它改寫成反射的機制。

相關文章