在不使用 imp.load_dynamic 的情况下将 DLL 导入 Python 3

4 投票
1 回答
4221 浏览
提问于 2025-04-18 09:26

目标

我想为Total Phase Aardvark的Python接口添加Windows支持,目前这个接口只支持Linux。这个接口是一个设备的封装,而这个设备的可用接口只有一个.so(Linux)或.dll(Windows)的闭源二进制文件。不过,它是作为一个Python包制作的(我不确定这个说法是否准确),而不是像你用ctypes加载的标准C接口。

文件结构

在这个项目中,我们有一个ext文件夹,它和执行导入的脚本在同一级别,里面有适用于Linux和Windows的32/64位库(这是我添加的):

pyaardvark.py (file doing imports)
ext
  linux32
    __init.py__ (empty)
    aardvark.so
  linux 64
    __init.py__ (empty)
    aardvark.so
  win32
    __init.py__ (empty)
    aardvark.dll
  win64
    __init.py__ (empty)
    aardvark.dll

导入问题

Linux的实现使用了:

from .ext.linux32 import aardvark as api

我最初在Python 2.7中测试添加Windows导入时,发现相对路径的导入方式无法正常工作。唯一成功的方法是:

import imp
import os
cur_path = os.path.dirname(__file__)
api = imp.load_dynamic('aardvark', os.path.join(cur_path, 'ext', 'win32', 'aardvark.dll'))

这个方法看起来不太好,但在Python 2.7中运行得很好,所有的API都可以使用。

我切换到Python 3.4进行测试,发现imp模块已经被弃用了。而且,从Python 3.2开始,imp模块似乎就没有load_dynamic这个功能了。我在Python 3.4中找不到让这个DLL可用的方法。

尝试的方法

方法1

在Python 2.7和Python 3.4中都失败了。

from .ext.win32 import aardvark as api

方法2

在Python 2.7和Python 3.4中都失败了。

import importlib
import os
cur_path = os.path.dirname(__file__)
api = importlib.import_module('aardvark', os.path.join(cur_path, 'ext', 'win32', 'aardvark.dll'))

方法3

在Python 2.7中有效,但在Python 3.4中失败。

import imp
import os
cur_path = os.path.dirname(__file__)
api = imp.load_dynamic('aardvark', os.path.join(cur_path, 'ext', 'win32', 'aardvark.dll'))

方法4

在Python 2.7和Python 3.4中都没有报错,但也不是我想要的结果。

import os
cur_path = os.path.dirname(__file__)
from ctypes import cdll
api = cdll.LoadLibrary(os.path.join(cur_path, 'ext', 'win32', 'aardvark.dll'))

在Python 2.7中使用有效的(imp.load_dynamic)导入后,dir(api)给我的结果是:

['__doc__', '__file__', '__name__', '__package__', 'py_aa_async_poll', 'py_aa_close', 'py_aa_configure', 'py_aa_features', 'py_aa_find_devices', 'py_aa_find_devices_ext', 'py_aa_gpio_change', 'py_aa_gpio_direction', 'py_aa_gpio_get', 'py_aa_gpio_pullup', 'py_aa_gpio_set', 'py_aa_i2c_bitrate', 'py_aa_i2c_bus_timeout', 'py_aa_i2c_free_bus', 'py_aa_i2c_monitor_disable', 'py_aa_i2c_monitor_enable', 'py_aa_i2c_monitor_read', 'py_aa_i2c_pullup', 'py_aa_i2c_read', 'py_aa_i2c_read_ext', 'py_aa_i2c_slave_disable', 'py_aa_i2c_slave_enable', 'py_aa_i2c_slave_read', 'py_aa_i2c_slave_read_ext', 'py_aa_i2c_slave_set_response', 'py_aa_i2c_slave_write_stats', 'py_aa_i2c_slave_write_stats_ext', 'py_aa_i2c_write', 'py_aa_i2c_write_ext', 'py_aa_i2c_write_read', 'py_aa_log', 'py_aa_open', 'py_aa_open_ext', 'py_aa_port', 'py_aa_sleep_ms', 'py_aa_spi_bitrate', 'py_aa_spi_configure', 'py_aa_spi_master_ss_polarity', 'py_aa_spi_slave_disable', 'py_aa_spi_slave_enable', 'py_aa_spi_slave_read', 'py_aa_spi_slave_set_response', 'py_aa_spi_write', 'py_aa_status_string', 'py_aa_target_power', 'py_aa_unique_id', 'py_aa_version', 'py_version']

使用ctypes导入后,dir(api)给我的结果是:

['_FuncPtr', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_func_flags_', '_func_restype_', '_handle', '_name']

我不知道接下来该尝试什么。我在Python 2.7中工作没问题,但我真的希望能支持Python 3。

1 个回答

0

我在使用Python 3.6时,通过importlib成功导入了一个外部库。官方文档提供了一个直接导入源文件的方法:

import importlib.util
import sys

# For illustrative purposes.
import tokenize
file_path = tokenize.__file__
module_name = tokenize.__name__

spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
# Optional; only necessary if you want to be able to import the module
# by name later.
sys.modules[module_name] = module

(https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly)

我把这个方法稍微改了一下:

import importlib.util

def load_dynamic(module, path):
    spec = importlib.util.spec_from_file_location(module, path)
    mod = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(mod)
    return mod

然后你就可以在Python 3中做类似这样的事情:

load_dynamic('aardvark', os.path.join(cur_path, 'ext', 'win32', 'aardvark.dll'))

撰写回答