利用Python CFFI進行原子級別的C庫調用

介紹

Python是一種高級語言,常用於快速開發、數據挖掘等領域,但有時候需要藉助C庫進行密集計算等操作。Python提供了很多種方式進行C庫調用,例如ctypes、Swig等,但各種方式都存在一些問題。CFFI是Python官方推薦的C庫調用方式,提供了原子級別的C庫調用能力,一致性強,靈活性高,效率較高,已被廣泛應用於NumPy、PyPy、Pillow等多個Python庫。

正文

一、CFFI的基礎使用

使用CFFI調用C庫的過程包括三步:

  1. 定義C函數介面
  2. from cffi import FFI
    
    ffi = FFI()
    
    lib = ffi.dlopen('libc.so.6')
    
    # 定義C函數介面
    # void 則表示沒有返回值
    # int 則表示返回值類型是整數
    # int, int 表示C函數接收兩個整數類型的參數
    add = lib.printf
    add.argtypes = ffi.typeof("char *")
    add.restype = ffi.typeof("int")
  3. 調用C函數介面
  4. # 調用C函數介面
    result = add(b"Hello World")
    print(result)
  5. 編譯代碼
  6. gcc -shared -o libtest.so test.c

二、CFFI與ctypes的比較

ctypes是Python內置的C庫調用方式,也是使用比較廣泛的一種方式,但與CFFI相比還是存在一些差異:

  • 使用方式:CFFI要求對C語言的代碼重構最小,而ctypes則要求用戶按照ctypes的規範來組織C代碼。
  • 兼容性:CFFI兼容性良好,支持多個平台,而ctypes不一定能夠在所有平台上正常使用。
  • Python版本:CFFI僅支持Python2.6、Python2.7、Python3.2以及更高版本,而ctypes作為Python內置庫則兼容Python2.6 ~ Python3.x各版本。

三、CFFI的高級使用

除了基本的C庫調用之外,CFFI還提供了一些高級功能,例如:

  • Struct類型的支持
  • from cffi import FFI
    
    ffi = FFI()
    
    cpp_code = '''
    #include 
    #include 
    
    struct PointF {
        float x;
        float y;
    };
    
    typedef struct PointF PointF;
    
    void show_point(PointF p) {
        printf("%0.1f, %0.1f", p.x, p.y);
    }'''
    
    # 聲明C數據結構
    ffi.cdef("""
        typedef struct {
            float x;
            float y;
        } PointF;
        void show_point(PointF);
    """)
    
    lib = ffi.verify(cpp_code, libraries=[])
    p = ffi.new('PointF*', [1.5, 2.4])
    lib.show_point(p[0])
  • Union類型的支持
  • from cffi import FFI
    
    ffi = FFI()
    
    cpp_code = '''
    #include 
    
    union Shape {
        int shape_type;
        struct {
            int x;
            int y;
            int width;
            int height;
        } rect;
    };
    
    typedef union Shape Shape;
    
    void show_rect(Shape r) {
        printf("(%d, %d, %d, %d)",
            r.rect.x, r.rect.y, r.rect.width, r.rect.height);
    }'''
    
    ffi.cdef("""
        typedef union {
            int shape_type;
            struct {
                int x;
                int y;
                int width;
                int height;
            } rect;
        } Shape;
        void show_rect(Shape);
    """)
    
    lib = ffi.verify(cpp_code, libraries=[])
    s = ffi.new('Shape*', [0])
    s.rect.x = 1
    s.rect.y = 2
    s.rect.width = 3
    s.rect.height = 4
    lib.show_rect(s[0])

代碼部分

以下是一個使用CFFI調用Windows API的例子:

# -*- coding: utf-8 -*-

from cffi import FFI

# 定義C函數介面
ffi = FFI()
ffi.cdef("""
    typedef struct _FILETIME {
        unsigned long dwLowDateTime;
        unsigned long dwHighDateTime;
    } FILETIME, *PFILETIME, *LPFILETIME;

    typedef struct _SYSTEMTIME {
        short wYear;
        short wMonth;
        short wDayOfWeek;
        short wDay;
        short wHour;
        short wMinute;
        short wSecond;
        short wMilliseconds;
    } SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME;

    void GetSystemTime(SYSTEMTIME *lpSystemTime);
    void SystemTimeToFileTime(const SYSTEMTIME *lpSystemTime, LPFILETIME lpFileTime);
    void FileTimeToLocalFileTime(CONST FILETIME *lpFileTime, LPFILETIME lpLocalFileTime);
    BOOL FileTimeToSystemTime(const FILETIME *lpFileTime, LPSYSTEMTIME lpSystemTime);
""")

lib = ffi.dlopen('kernel32.dll')

# 調用GetSystemTime函數,獲取當前系統時間(UTC)
system_time = ffi.new('SYSTEMTIME *')
lib.GetSystemTime(system_time)

# 將UTC時間轉換為本地時間
file_time_utc = ffi.new('FILETIME *')
lib.SystemTimeToFileTime(system_time, file_time_utc)
file_time_local = ffi.new('FILETIME *')
lib.FileTimeToLocalFileTime(file_time_utc, file_time_local)

# 獲取系統時間(本地時間)
local_time = ffi.new('SYSTEMTIME *')
lib.FileTimeToSystemTime(file_time_local, local_time)

print('Local Time:', local_time.wYear, local_time.wMonth, local_time.wDay, local_time.wHour, local_time.wMinute, local_time.wSecond, local_time.wMilliseconds)

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/257975.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-12-15 12:47
下一篇 2024-12-15 12:47

相關推薦

  • Python計算陽曆日期對應周幾

    本文介紹如何通過Python計算任意陽曆日期對應周幾。 一、獲取日期 獲取日期可以通過Python內置的模塊datetime實現,示例代碼如下: from datetime imp…

    編程 2025-04-29
  • 如何查看Anaconda中Python路徑

    對Anaconda中Python路徑即conda環境的查看進行詳細的闡述。 一、使用命令行查看 1、在Windows系統中,可以使用命令提示符(cmd)或者Anaconda Pro…

    編程 2025-04-29
  • Python中引入上一級目錄中函數

    Python中經常需要調用其他文件夾中的模塊或函數,其中一個常見的操作是引入上一級目錄中的函數。在此,我們將從多個角度詳細解釋如何在Python中引入上一級目錄的函數。 一、加入環…

    編程 2025-04-29
  • Python列表中負數的個數

    Python列表是一個有序的集合,可以存儲多個不同類型的元素。而負數是指小於0的整數。在Python列表中,我們想要找到負數的個數,可以通過以下幾個方面進行實現。 一、使用循環遍歷…

    編程 2025-04-29
  • Python周杰倫代碼用法介紹

    本文將從多個方面對Python周杰倫代碼進行詳細的闡述。 一、代碼介紹 from urllib.request import urlopen from bs4 import Bea…

    編程 2025-04-29
  • python強行終止程序快捷鍵

    本文將從多個方面對python強行終止程序快捷鍵進行詳細闡述,並提供相應代碼示例。 一、Ctrl+C快捷鍵 Ctrl+C快捷鍵是在終端中經常用來強行終止運行的程序。當你在終端中運行…

    編程 2025-04-29
  • Python字典去重複工具

    使用Python語言編寫字典去重複工具,可幫助用戶快速去重複。 一、字典去重複工具的需求 在使用Python編寫程序時,我們經常需要處理數據文件,其中包含了大量的重複數據。為了方便…

    編程 2025-04-29
  • Python清華鏡像下載

    Python清華鏡像是一個高質量的Python開發資源鏡像站,提供了Python及其相關的開發工具、框架和文檔的下載服務。本文將從以下幾個方面對Python清華鏡像下載進行詳細的闡…

    編程 2025-04-29
  • Python程序需要編譯才能執行

    Python 被廣泛應用於數據分析、人工智慧、科學計算等領域,它的靈活性和簡單易學的性質使得越來越多的人喜歡使用 Python 進行編程。然而,在 Python 中程序執行的方式不…

    編程 2025-04-29
  • 蝴蝶優化演算法Python版

    蝴蝶優化演算法是一種基於仿生學的優化演算法,模仿自然界中的蝴蝶進行搜索。它可以應用於多個領域的優化問題,包括數學優化、工程問題、機器學習等。本文將從多個方面對蝴蝶優化演算法Python版…

    編程 2025-04-29

發表回復

登錄後才能評論