Python反射
这篇文章没有系统地解释什么是反射以及完整的Python反射机制,仅仅作为我之前使用反射解决问题的一个记录。
假设我们有以下文件:
E:.
│
├─package1
│ main.py
│ people.py
│ __init__.py
│
└─package2
animal.py
__init__.py
package1.people
中的代码如下所示:
class People:
def __init__(self,name):
print("My name is {}".format(name))
def say_hello(self,words):
print(words)
def write(words):
print("Writing:{}".format(words))
该文件中定义了一个People
类,该类有一个含参构造函数和成员函数say_hello
。people
模块中有一个函数write
package2.animal
中的代码如下所示:
class Cat:
def __init__(self,name):
print("This is a cat named {}".format(name))
def roar(self):
print("meow meow meow")
该模块中仅有Cat
类,其内部有一个含参构造函数和一个成员函数roar
我们在package1.main
中编写reflection
相关的代码。假设我们有以下需求:
A. 我们需要动态引入package1.people
中的People
类和write
函数
import inspect
import types
from inspect import isfunction
"""The same package"""
# get class in module
module = __import__("people")
people_clazz = getattr(module,"People")
write_func = getattr(module,"write")
# create instance by reflection
print("Create an instance of people")
args = {"name":"xiaoming"}
p = people_clazz(**args)
# invoke function by reflection
write_func("An apple a day keep the doctor away!")
已知people
和当前模块main
在同一个包路径下,因此我们直接使用__import__("people")
来引入people
模块。同时,使用我们使用getattr
来获取module中的People
类和write
函数。
由于People
类和write
函数都是callable
的,因此我们可以直接调用people_clazz
和write_func
Console Output:
Create an instance of people
My name is xiaoming
Writing:An apple a day keep the doctor away!
B. 获取一个module中所有函数(除去module
自身的builtin
的函数)
# get function of module
module_funcs = [func[0] for func in inspect.getmembers(module) if isinstance(func[1],types.FunctionType)]
# module_funcs = [func[0] for func in inspect.getmembers(module) if isfunction(func[1])] # the same as code above
print("Get functions from module",module_funcs)
inspect是Python中一个专门用于查询module
,class
,function
等live objects
信息的模块。
我们获取people
模块所有的成员函数/变量,并判断其是不是Function
类型。
注意,注释部分的代码和其上方的代码功能是一样的。isfunction
内部的实现也正是如此。
inspect.getmembers(module)
返回的是一个list
,其中的每个元素为tuple
类型。tuple
的第一个元素为变量名,第二个为变量类型。
Console Output:
Get functions from module [‘write’]
C. 获取一个类中所有的函数(类的魔法函数除外)
# get function of class
people_clazz_func = [func[0] for func in inspect.getmembers(people_clazz) if not func[0].startswith("_")]
print("Get member function of class people",people_clazz_func)
同上面的代码。
注意,这里我判断过滤掉了那些以_
开头的函数,以此来过滤掉类的魔法函数。但是这样也会过滤掉自身定义的以_
开头的函数。
Console Output:
Get member function of class people [‘say_hello’]
D. 获取一个函数的参数:
# get arguments of function
people_init_args = inspect.getargs(people_clazz.__init__.__code__)
print("Get arguments from class member function",people_init_args.args)
write_args = inspect.getargs(write_func.__code__)
print("Get arguments from module function",write_args.args)
这里我展示了两种类型的函数。一个是类的成员函数,一个是普通的函数。用法都是一样的,使用inspect.getargs
和 函数的__code__
属性来获取参数变量。
注意,如果想获取列表类型的参数,需要使用args
属性。
Console Output:
Get arguments from class member function [‘self’, ‘name’]
Get arguments from module function [‘words’]
E. 被引入模块和当前文件不在同一个包内
"""Different package"""
module_package, cat_class = "package2.animal", "Cat"
module = __import__(module_package, globals(), locals(), [cat_class])
cat_clazz = getattr(module, cat_class)
args = {"name":"puppy"}
c = cat_clazz(**args)
c.roar()
稍微复杂了一些,但是原理是一样的,只是__import__
多传入了包名以及globals(), locals()
等参数。
Console Output:
This is a cat named puppy
meow meow meow