⑴ 如何在python实现程序加载插件(动态加载模块文件,插件化开发)
在Python中实现程序加载插件(动态加载模块文件,插件化开发)
动态加载插件是许多软件,如pycharm,Minecraft,泰拉瑞亚,饥荒,vim,vscode的常见功能,让它们能够拓展软件本身功能。Python模块的格式为.py(或.pyd),这为动态加载提供了便利。
实现动态导入模块,通常有两种方式:使用import关键字和使用__import__函数,但它们都存在限制。import关键字加载的模块名必须确定,且使用pyinstaller或nuke打包时不允许模块名为空。而使用__import__函数虽官方不推荐,但它是一种实现方式。
推荐使用importlib模块,它是import的实现,功能强大且使用简单,通过importlib.import_mole参数可以指定包和模块。使用相对路径导入时需注意exe文件的位置。动态导入上层模块时,可以通过修改sys.path列表来添加搜索路径,使用'..'或绝对路径。
动态寻找插件时,可以使用os.path或pathlib模块来管理文件。实现流程包括扫描指定插件文件夹下的所有文件和文件夹,过滤出所需文件,然后将文件转换为可以传入importlib.import_mole()参数的形式。
示例:实现扫描并导入插件文件夹中的插件文件。此操作可以在主程序文件中执行,例如在__main__1.py或__main__2.py文件中,实现扫描指定文件夹并导入文件夹中的插件。在项目中,可参考hpyculator/calculate_manager.py实现动态加载插件。
具有插件加载功能的库/软件包括:Fallen-Breath/MCDReforged、nonebot/nonebot2、HowieHz/hpyculator等。写文章初次尝试,欢迎各位大佬提出宝贵意见。
⑵ python怎么调用c的动态链接库
Python调用C/C++动态链接库的需求
在自动化测试过程中,难免会遇到语言混合使用的情况,这不,我们也遇到了。初步决定采用Robot Framework作为自动化测试框架后,其支持Java和Python,而Python作为主流的语言,怎么能放弃使用它的机会^_^。 然而产品采用是古老90年代开发的C/S结构,因为古老,当时也没有考虑到对产品的测试进行自动化,Client端并没有预留CLI(Command Line interface)形式的接口,真是雪上加霜啊。
那怎么自动化?采用AutoIT来对客户端界面进行自动化测试?可惜AutoIT对当初开发采用的控件识别不是很好,如果采用控件所在位置来进行控制的方式,又会导致自动化测试并不是很稳定。那么!!!只有自己开发接口了,目前在Client端开发出CLI形式的接口,将其封装为DLL,然后在Robot FrameWork框架中采用Python对DLL进行调用。任务艰巨哪!
Python调用DLL例子
示例一
首先,在创建一个DLL工程(本人是在VS 2005中创建),头文件:
[cpp] view plain 在CODE上查看代码片派生到我的代码片//hello.h
#ifdef EXPORT_HELLO_DLL
#define HELLO_API __declspec(dllexport)
#else
#define HELLO_API __declspec(dllimport)
#endif
extern "C"
{
HELLO_API int IntAdd(int , int);
}
CPP文件:
[cpp] view plain 在CODE上查看代码片派生到我的代码片//hello.cpp
#define EXPORT_HELLO_DLL
#include "hello.h"
HELLO_API int IntAdd(int a, int b)
{
return a + b;
}
这里有两个注意点:
(1)弄清楚编译的时候函数的调用约定采用的__cdecl还是__stdcall,因为根据DLL中函数调用约定方式,Python将使用相应的函数加载DLL。
(2)如果采用C++的工程,那么导出的接口需要extern "C",这样python中才能识别导出的函数。
我的工程中采用__cdecl函数调用约定方式进行编译链接产生hello.dll,然后Python中采用ctypes库对hello.dll进行加载和函数调用:
[python] view plain 在CODE上查看代码片派生到我的代码片from ctypes import *
dll = cdll.LoadLibrary('hello.dll');
ret = dll.IntAdd(2, 4);
print ret;
OK,一个小例子已经完成了,如果你感兴趣,但还没试过,那就尝试一下吧。
示例二
示例一只是一个"hello world"级别的程序,实际运用中更多的需要传递数据结构、字符串等,才能满足我们的需求。那么这个示例将展示,如何传递数据结构参数,以及如何通过数据结构获取返回值。
首先编写DLL工程中的头文件:
[cpp] view plain 在CODE上查看代码片派生到我的代码片//hello.h
#ifdef EXPORT_HELLO_DLL
#define HELLO_API __declspec(dllexport)
#else
#define HELLO_API __declspec(dllimport)
#endif
#define ARRAY_NUMBER 20
#define STR_LEN 20
struct StructTest
{
int number;
char* pChar;
char str[STR_LEN];
int iArray[ARRAY_NUMBER];
};
extern "C"
{
//HELLO_API int IntAdd(int , int);
HELLO_API char* GetStructInfo(struct StructTest* pStruct);}
CPP文件如下:
[cpp] view plain 在CODE上查看代码片派生到我的代码片//hello.cpp
#include <string.h>
#define EXPORT_HELLO_DLL
#include "hello.h"
HELLO_API char* GetStructInfo(struct StructTest* pStruct){
for (int i = 0; i < ARRAY_NUMBER; i++)
pStruct->iArray[i] = i;
pStruct->pChar = "hello python!";
strcpy (pStruct->str, "hello world!");
pStruct->number = 100;
return "just OK";
}
GetStructInfo这个函数通过传递一个StructTest类型的指针,然后对对象中的属性进行赋值,最后返回"just OK".
编写Python调用代码如下,首先在Python中继承Structure构造一个和C DLL中一致的数据结构StructTest,然后设置函数GetStructInfo的参数类型和返回值类型,最后创建一个StructTest对象,并将其转化为指针作为参数,调用函数GetStrcutInfo,最后通过输出数据结构的值来检查是否调用成功:
[python] view plain 在CODE上查看代码片派生到我的代码片from ctypes import *
ARRAY_NUMBER = 20;
STR_LEN = 20;
#define type
INTARRAY20 = c_int * ARRAY_NUMBER;
CHARARRAY20 = c_char * STR_LEN;
#define struct
class StructTest(Structure):
_fields_ = [
("number", c_int),
("pChar", c_char_p),
("str", CHARARRAY20),
("iArray", INTARRAY20)
]
#load dll and get the function object
dll = cdll.LoadLibrary('hello.dll');
GetStructInfo = dll.GetStructInfo;
#set the return type
GetStructInfo.restype = c_char_p;
#set the argtypes
GetStructInfo.argtypes = [POINTER(StructTest)];objectStruct = StructTest();
#invoke api GetStructInfo
retStr = GetStructInfo(byref(objectStruct));#check result
print "number: ", objectStruct.number;
print "pChar: ", objectStruct.pChar;
print "str: ", objectStruct.str;
for i,val in enumerate(objectStruct.iArray):
print 'Array[i]: ', val;
print retStr;
总结
1. 用64位的Python去加载32位的DLL会出错
2. 以上只是些测试程序,在编写Python过程中尽可能的使用"try Except"来处理异常3. 注意在Python与C DLL交互的时候字节对齐问题4. ctypes库的功能还有待继续探索
⑶ python调用dll找不到模块
在Python中调用DLL(动态链接库)时遇到“找不到模块”的错误通常是由于几种常见原因引起的。这些问题可能涉及到路径问题、依赖问题、位数不匹配
原因和解决策略:
1. 路径问题:如果DLL文件不在程序的搜索路径中,Python就无法找到它。确保DLL路径被正确添加到系统路径,或者通过代码指定路径。
2. 依赖问题:DLL可能依赖于其他库,如果这些库没有被正确安装或者没有放在正确的路径中,就会导致加载失败。使用工具如Dependency Walker (适用于Windows) 来检查DLL的依赖,并确保所有依赖都已经安装并位于可搜索的路径中。
3. 位数不匹配问题:如果Python解释器的位数(32位或64位)与DLL的位数不匹配,也会导致加载失败。确保Python解释器和DLL的位数相同。如果你使用的是64位Python,也必须使用64位的DLL。
4. 检查错误代码:当ctypes无法加载DLL时,尝试捕获异常并打印出更多错误信息可能会有帮助。