该函数用于对传入的表达式进行解析和评估,原型如下:
eval(expression, globals=None, locals=None)
# globals:如果指定,必须为一个字典,表示全局上下文
# locals:如果指定,必须为一个映射类型,表示本地上下文
在前面分析repr()函数时说过,会根据repr()的返回值评估一个对象,如:
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return "Student('{0}',{1})".format(self.name,self.age)
if __name__ == "__main__":
s1 = Student("zhangsan",21)
s2 = eval(repr(s1))
print(s2.name) # zhangsan
除了这种用法之外,还可以执行一般的表达式,如:
result = eval("2+3") # 5
eval()可以执行表达式,那么如果要动态执行一个函数时,这个方法时不行的,可以使用exec()来实现。
global()和locals()函数分别返回当前的本地和全局字典。
该方法支持动态执行函数代码,原型如下:
exec(object[, globals[, locals]])
对象必须为一个字符串或者来自于compile()
的代码对象,返回值为None,对于可选的global和locals参数,如果提供,则分别为全局上下文和本地上下文。
如果在调用exec()时仅仅将object作为其唯一参数,那么没有途径可以获取该代码执行后创建的任何函数或数量,而且exec()函数不能存取任意导入的模块、变量、函数、其他对象,因此,需要传入一个字典类型作为第二个参数,该参数提供了存放对象引用的场所,当执行完exec()函数后,会将所有信息存放在该字典中,比如:
# 定义一个函数字符串
fun = '''
def area(radius):
return math.pi * (radius ** 2)
'''
# 在exec()执行过程中存放数据的dict
context = {}
# name 'math' is not defined
# 由于exec()执行时并不能存取导入模块,因此需要将该模块存储在dict中
context["math"] = math
result1 = exec(fun, context) # None
result2 = context["area"]
ar = result2(3) # 28.274333882308138
当exec(fun,context)后,context中将会以area为key,以area()函数对象的引用为值进行存储。
global()和locals()函数分别返回当前的本地和全局字典,因此,将global()函数作为其第二个参数是很方便的一种方法,但如果这样,则exec()执行时生成的所有的对象都会被添加到全局字典中。所以可以使用全局字典的拷贝来解决,如将上例中的context改为全局字典:
fun = '''
def area(radius):
return math.pi * (radius ** 2)
'''
con = globals().copy()
exec(fun, con)
result = con["area"](4)
print(result) # 50.26548245743669
这里使用两个方法实现动态导入模块,分别为使用代码实现动态导入和使用__import__()
方法进行动态导入,先来看使用代码实现动态导入:
def load_modules():
modules = []
# 获取当前目录下所有.py文件
for filename in os.listdir("."):
if filename.endswith(".py") and not filename.startswith("dynamic_import_self"):
# 拆分扩展名
name = os.path.splitext(filename)[0]
if name.isidentifier():
fh = None
try:
fh = open(filename, "r", encoding="utf8")
code = fh.read()
# 得到一个模块对象,并通过传入的name新建一个模块对象
# type()方法返回参数对应类型的对象,如type(2)返回一个int对象
module = type(sys)(name)
# 将新创建的module添加到sys.modules中,sys.modules是一字典,存储已加载的模块,以模块名为key,模块为值
sys.modules[name] = module
# 通过exec()动态执行代码,并将生成的属性、对象和所需模块放在module.__dict中
# 每个module对象都有一个特殊属性__dict,用于存储对象属性的字典
exec(code, module.__dict__)
modules.append(module)
except (EnvironmentError, SyntaxError) as err:
# 如果发生异常,从sys.modules字典中移除该module对象
sys.modules.pop(name, None)
print(err)
finally:
if fh is not None:
fh.close()
return modules
这种方式从头到尾是通过os模块和sys模块中提供的一些方法,将模块遍历并进行读取,最终得到module对象。第二种方法就很简单了,只需要通过__import__()
方法就可以实现,如下:
try:
module = __import__(name)
modules.append(module)
except (EnvironmentError, SyntaxError) as err:
print(err)
当动态导入模块后,就可以使用该模块中的属性和函数了,那么如何使用呢?python提供了用于操作模块属性的函数,如下:
getattr(obj,name,val)
:返回对象obj中名为name的属性值,如果没有该属性,且给定了val参数,则返回val。
hasattr(obj,name)
:如果对象obj中存在名为name的属性,则返回True。
delattr(obj,name)
:删除obj中名为name的属性
setattr(obj,name,val)
:将对象obj中名为name的属性值设置为val,如果不存在该属性,则进行创建。
下面是判断是否有“exit”方法,并返回:
def get_function(module):
# 从module中获取exit属性
function = getattr(module,"exit")
if function is not None:
try:
# 如果这个属性是可调用对象,所有的可调用对象即函数和方法,
# 都有__call__属性
if not hasattr(function,"__call__"):
raise AttributeError()
function(0) # sys.exit(0)
except AttributeError as err:
print(err)
function = None
return function
至此,对动态代码执行、动态模块导入,以及导入模块后调用模块方法有了眉目了,如果仅仅是执行一个表达式,则eval()函数即可,当然exec()也可以;如果要执行一个函数,就需要使用exec()函数来动态执行了。如果需要动态导入模块,则__import__()
函数会将函数快速的导入。