反射机制是通过python3内置的hasattr、getattr、setattr来实现的。即根据变量名的字符串形式来获取变量名的属性或方法。
一、通过反射查看已知对象的属性和方法
getattr(object, name[, default]) -> value
Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y. When a default argument is given, it is returned when the attribute doesn't exist; without it, an exception is raised in that case.
getattr接收三个参数:对象,对象的属性或方法,以及未获取到时的默认返回值。
class MyClass(object): country = "China" def __init__(self, name, age): self.name = name self.age = age self._gender = None def hello(self): print("I'm {}, {}.".format(self.name, self.age)) def gender(self, gender): self._gender = gender print(self._gender) print(getattr(MyClass, "country")) # 获取对象的静态属性 my = MyClass("Li", 24) print(getattr(my, "name")) # 获取实例的变量(属性) getattr(my, "hello")() # 获取并执行实例的方法 getattr(my, "gender")("female") # 传入参数
二、通过反射获取模块的方法
1.访问当前模块的对象
def func(): func.name = "Li" func.age = 24 print("I'm {}, {}.".format(func.name, func.age)) if __name__ == '__main__': import sys # 需要引入sys模块,用sys.modelus["__main__"]访问当前模块的内存地址 getattr(sys.modules["__main__"], "func")() # 从模块中获取属性 print(getattr(func, "name"))
2.访问其它模块的对象
将上面的代码保存到test1.py文件中,在test2.py中导入test1.py,同样可以访问MyClass类。
import test1 if __name__ == '__main__': MyClass = getattr(test1, "MyClass") # 这里也可以用sys.modules["test1"]来替代test1 print(MyClass.country) my = MyClass("Li", 24) print(getattr(my, "name")) # 获取实例的变量(属性) getattr(my, "hello")() # 获取并执行实例的方法 getattr(my, "gender")("female") # 传入参数
三、__import__和importlib.import_module
python3提供了一个特殊的方法:__import__(字符串参数)。__import__()方法会根据参数,动态的导入同名的模块。它的功能和getattr相似。
将test2.py中的代码改为下面两行,即可实现同样的功能。
module = __import__("test1") fun = getattr(module, "func")()
或者这样写:
import importlib module = importlib.import_module("test1") fun = getattr(module, "func")()
它们都是根据模块名来访问该模块的内存空间,从而获取全局变量、函数或者类等对象。
来看一段文档介绍:
Docstring:
__import__(name, globals=None, locals=None, fromlist=(), level=0) -> module
Import a module. Because this function is meant for use by the Python interpreter and not for general use,
it is better to use importlib.import_module() to programmatically import a module.
# 导入模块应该用importlib.import_module(),__import__是提供给Python解释器使用的。
# globals和locals、level参数可以忽略。
The fromlist should be a list of names to emulate ``from name import ...'', or an empty list to emulate ``import name''.
When importing a module from a package, note that __import__('A.B', ...) returns package A when fromlist is empty, but its submodule B when fromlist is not empty.
# fromlist是a list of names blabla。但是试了几次,好像只能用True来设置,其它不起作用
module = __import__("test1", ) # 它相当于import test1 # module = __import__("test.person", fromlist=True) # 它相当于from test import person # 从模块中导入py文件,test是一个package,包含__init__.py和person.py # person.py中包含MyClass类 Myclass = getattr(module, "MyClass") my = Myclass("Li", 24) my.hello()
importlib的用法也相似。
import importlib # module = importlib.import_module("test1") # import test1 # module = importlib.import_module("test.person", package=True) # from test import person module = importlib.import_module("test_outer.test.person", package=True) # from test_outer.test import person Myclass = getattr(module, "MyClass") my = Myclass("Li", 24) my.hello()
四、例子
1.遍历模块查找所需函数
# 文件夹 test - __init__.py - drink.py # 定义一个drink函数,打印"I'm drinking." - person.py - say.py # 同drink.py - sleep.py # 同drink.py
person.py
import os import importlib class Person: def __init__(self, name): self.name = name self.modules = self.py_list() def py_list(self): pys = os.listdir(os.path.dirname(__file__)) modules = [] for py in pys: if py.endswith(".py") and not py.startswith("__"): modules.append(importlib.import_module(py.split(".")[0])) # 将多个模块全部导入到一个list中 return modules def action(self): while True: imp = input("小明 >>> ") fun = None for module in self.modules: # 遍历查询module,查找imp函数 if hasattr(module, imp): fun = getattr(module, imp) break if fun: fun() else: print("Order isn't correct.") if __name__ == '__main__': per = Person("Li") per.action()
2.动态导入模块
当然,也可以根据模块名和对象名,来获取相应的模块,并调用相应的方法。从而不必将所有的模块都导入进来。
import importlib class Person: def __init__(self, name): self.name = name def action(self): while True: imp = input("小明 >>> ") try: module, fun = imp.split("/") module = importlib.import_module(module,) if hasattr(module, fun): getattr(module, fun)() else: print("Order isn't correct.") except: print("input not correct.") if __name__ == '__main__': per = Person("Li") per.action() # 需要输入sleep/sleep 或者drink/drink
3.setattr的使用
import importlib class Person: def __init__(self, name): self.name = name def sleep(self): getattr(importlib.import_module("sleep"), "sleep")() def drink(self): getattr(importlib.import_module("drink"), "drink")() def fun(self, action): if hasattr(self, action): getattr(self, action) else: setattr(self, action, self.error) # 设置aciton的函数,当然可以在上面的getattr的default关键字中设置 getattr(self, action)() # 调用action的函数 def error(self): print("error.") if __name__ == '__main__': per = Person("Li") per.fun("wth")