ctypes.CDLL() 和 ctypes.cdll.LoadLibrary() 有什么区别?

ner*_*com 8 python ctypes

这两种方法似乎都有效(对我来说),但似乎该CDLL()方法返回一个具有_handle属性的对象,该对象可用于通过ctypes.windll.kernel32.FreeLibrary()(至少在 Windows 上卸载库 - 我还不知道如何在 Linux 上执行此操作) )。

这两种方法有什么区别 - 为什么我要选择其中一种而不是另一种?

最终,我的目标是能够在 Linux 和 Windows 上加载和卸载库(因为我有一个第三方库,有时似乎会进入损坏状态 - 我希望卸载/重新加载能够重置它)。

Cri*_*ati 5

一切都在[Python.Docs] 中得到了很好的解释:ctypes - Python 的外部函数库

\n
\n

ctypes。CDLL (名称、模式=DEFAULT_MODE、句柄=无、use_errno=False、use_last_error=False、winmode=0 )
\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0 \xc2\xa0 此类的实例表示加载的共享库。这些库中的函数使用标准 C 调用约定,并假定返回int

\n

...

\n

ctypes。库加载器dll类型

\n

...

\n

LoadLibrary ( name )
\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0将共享库加载到进程中并返回。此方法始终返回库的新实例。

\n

这些预制库加载器可用:

\n

ctypes。cdll
\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0创建CDLL实例。

\n
\n

所以,第二种形式只是一个方便的包装器,它们之间绝对没有功能上的区别,如下所示:

\n
    \n
  • \n
    \n
    [cfati@CFATI-5510-0:e:\\Work\\Dev\\StackOverflow\\q067049436]> "e:\\Work\\Dev\\VEnvs\\py_pc064_03.10_test0\\Scripts\\python.exe"\nPython 3.10.9 (tags/v3.10.9:1dd9be6, Dec  6 2022, 20:01:21) [MSC v.1934 64 bit (AMD64)] on win32\nType "help", "copyright", "credits" or "license" for more information.\n>>>\n>>> import ctypes as cts\n>>>\n>>>\n>>> k32_1 = cts.CDLL("kernel32.dll")  # 1.\n>>> k32_21 = cts.cdll.LoadLibrary("kernel32.dll")  # 2.1.\n>>> k32_22 = cts.cdll.kernel32  # 2.2.\n>>>\n>>> k32_1, k32_21, k32_22\n(<CDLL \'kernel32.dll\', handle 7fff59100000 at 0x2335c444ee0>, <CDLL \'kernel32.dll\', handle 7fff59100000 at 0x2335b44bc10>, <CDLL \'kernel32\', handle 7fff59100000 at 0x2335c45a790>)\n>>>\n>>> type(k32_1), type(k32_21), type(k32_22)\n(<class \'ctypes.CDLL\'>, <class \'ctypes.CDLL\'>, <class \'ctypes.CDLL\'>)\n>>>\n>>> k32_1._handle == k32_21._handle == k32_22._handle\nTrue\n
    Run Code Online (Sandbox Code Playgroud)\n
    \n

    从技术上讲,这里应该使用WinDLLwindll ),但由于Python064位pc064),CDLLcdll)也可以

    \n
  • \n
  • 尼克斯

    \n
    \n
    (qaic-env) [cfati@cfati-5510-0:/mnt/e/Work/Dev/StackOverflow/q067049436]> ll $(pwd)/../q074171783/*.so\n-rwxr-xr-x 1 cfati cfati 16376 Oct 23 22:37 /mnt/e/Work/Dev/StackOverflow/q067049436/../q074171783/dll00.so*\n-rwxr--r-- 1 cfati cfati  9728 Oct 23 22:40 /mnt/e/Work/Dev/StackOverflow/q067049436/../q074171783/dll00_wincopy.so*\nlrwxrwxrwx 1 cfati cfati     8 Jan 11 10:50 /mnt/e/Work/Dev/StackOverflow/q067049436/../q074171783/libdll00.so -> dll00.so*\n(qaic-env) [cfati@cfati-5510-0:/mnt/e/Work/Dev/StackOverflow/q067049436]>\n(qaic-env) [cfati@cfati-5510-0:/mnt/e/Work/Dev/StackOverflow/q067049436]> LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:$(pwd)/../q074171783 python\nPython 3.8.10 (default, Nov 14 2022, 12:59:47)\n[GCC 9.4.0] on linux\nType "help", "copyright", "credits" or "license" for more information.\n>>>\n>>> import ctypes as cts\n>>>\n>>>\n>>> ld0_1 = cts.CDLL("libdll00.so")  # 1.\n>>> ld0_21 = cts.cdll.LoadLibrary("libdll00.so")  # 2.1.\n>>> #ld0_22 = cts.cdll.libdll00  # 2.2.\n>>>\n>>> ld0_1, ld0_21\n(<CDLL \'libdll00.so\', handle 11f9510 at 0x7fbb9576cca0>, <CDLL \'libdll00.so\', handle 11f9510 at 0x7fbb95754eb0>)\n>>>\n>>> type(ld0_1), type(ld0_21)\n(<class \'ctypes.CDLL\'>, <class \'ctypes.CDLL\'>)\n>>>\n>>> ld0_1._handle == ld0_21._handle\nTrue\n
    Run Code Online (Sandbox Code Playgroud)\n
    \n

    #2.2。(注释)在这里不起作用,因为文件扩展名必须是传递给 [Man7] 的字符串的一部分:DLOPEN (3)(否则它找不到它 - 也许这可以解决Ld脚本? )。
    \n我能找到的最接近的东西是cts.cdll["libdll00.so"](但它看起来仍然与其他选项更相关)

    \n
  • \n
\n

使用最适合你的东西。
第二种形式(#2.2.)较短(我想这就是它的目的)#1. #2.1。是相同的(#2.1.可能更具解释性(因为它有LoadLibrary)),并且它们允许您从自定义路径加载库,或者使用与默认路径不同的扩展名。就个人而言,#1。是我更喜欢的。

\n

更多细节,你可以看一下[GitHub]:python/cpython - (master) cpython/Lib/ctypes/__init__.py,特别是LibraryLoader的实现(实际上是cdll)很容易理解。

\n

请注意(可能您已经知道自己要做什么):加载和卸载库有时可能很棘手:

\n\n

您可能还想检查[SO]:通过 ctypes 从 Python 调用的 C 函数返回不正确的值(@CristiFati\'s 答案),以了解通过CTypes调用函数时遇到的常见陷阱。

\n