您的当前位置:首页Python动态从文件中导入类或函数的方法

Python动态从文件中导入类或函数的方法

2023-02-24 来源:乌哈旅游
Python动态从⽂件中导⼊类或函数的⽅法

假设模块⽂件名是data_used_to_test.py,放在tests⽂件夹下⽂件夹结构如下:

project |-tests

|-data_used_to_test.py

⽂件内包含⼀个test_class类:

class test_class(): def test_func(arg):

return \"hello {}\".format(arg)

代码全部基于 Python3.6.41. 使⽤imp

1. ⽤imp.find_module查找模块

In [1]:file, pathname, description = imp.find_module('data_used_to_test', path=['tests/'])In [2]: file

Out[2]: <_io.TextIOWrapper name='tests/data_used_to_test.py' mode='r' encoding='utf-8'>In [3]: pathname

Out[3]: 'tests/data_used_to_test.py'In [4]: descriptionOut[4]: ('.py', 'r', 1)

2. ⽤imp.load_module将找到的模块加载到sys.modules中

In [5]: mod = imp.load_module('test_load', file, pathname, description)In [6]: mod

Out[6]:

这时候sys.modules⾥会多⼀条'test_load'的记录,值就是mod的值。3. 这时候就可以直接通过mod访问包内的对象了

In [7]: mod.test_class().test_func('x')Out[7]: 'hello x'

这个⽅法的优点是

1. 简单,容易实现。

2. 不⽤对pyc⽂件做特殊处理缺点是可定制性太低。不适合框架使⽤

1. ⽆法动态修改模块源代码, 开放的api必须要很稳定,不会经常变动。2. 可以访问的对象⼀开始就要制定成固定名称。⽆法动态注册访问。4. 上⾯的问题2可以⽤getattr解决,让我们更进⼀步。

In [8]: tmp = getattr(mod, 'test_class')In [9]: tmp

Out[9]: test_load.test_classIn [10]: tmp().test_func('l')Out[10]: 'hello l'

这样就可以通过事先在外部模块中调⽤准备好的注册函数,把外部模块中的类或函数注册到⼀个全局的单例变量中,实现动态的模块加载和对象访问。但仍然⽆法解决问题 1.

所以就需要另⼀种模块加载⽅法。

2. 显式的定义⼀个加载函数

import imp

import sys

def load_module(module_name, module_content): if fullname in sys.modules:

return sys.modules[fullname]

mod = sys.modules.setdefault(url, imp.new_module(module_name)) mod.__file__ = module_name mod.__package__ = ''

# if *.py

code = compile(module_content, url, 'exec') # if *.pyc 有问题,我运⾏⼀直报错

# code = marshal.loads(module_content[8:])

exec(code, mod.__dict__)

# 2的写法是 exec code in mod.__dict__

# 其实就是让code在环境⾥运⾏⼀下,所以这⾥可能会有注⼊漏洞 return mod

这个函数接受模块的名字和源码内容,并使⽤ compile() 将其编译到⼀个代码对象中, 然后在⼀个新创建的模块对象的字典中来执⾏它。

下⾯是这个函数的使⽤⽅式:

In[1]: module_content = open('tests/data_used_to_test.py').read()

In[2]: mod = load_module('test_import', module_content)

后⾯就和 1.4 ⼀样了。

这样可以同时解决 1.3 中提出的两个问题。

因为你是先将源码作为普通⽂件读进来的,也就可以做各种修改后再注册到sys.modules中。Pocsuite使⽤的就是这种⽅法。虽然我觉得他们当时可能也没太弄明⽩。当然这还是有不⾜的。

1. 上⾯的两种⽅法都只⽀持简单的模块。并没有嵌⼊到通常的import语句中。并不⽀持更⾼级的结构⽐如包。2. 不够酷3. ⾃定义导⼊器

PythonCookbook上给了⾃定义导⼊器的两种⽅式1. 创建⼀个元路径导⼊器

2. 编写⼀个钩⼦直接嵌⼊到 sys.path 变量中去

还没看明⽩,PEP302的⽂档太长了,⼤概知道是继承importlib.abc来做的。

因篇幅问题不能全部显示,请点此查看更多更全内容