Trăn con trỏ thông minh

Đây là một hướng dẫn ngắn về cách sử dụng C từ Python với sự trợ giúp của mô-đun trình bao bọc ctypes. Giả sử chúng ta có một thư viện C nhỏ để tính tổng và muốn sử dụng nó trong Python

Tổng. c

int our_function(int num_numbers, int *numbers) {
    int i;
    int sum = 0;
    for (i = 0; i < num_numbers; i++) {
        sum += numbers[i];
    }
    return sum;
}

biên dịch nó

cc -fPIC -shared -o libsum.so sum.c

Bây giờ đến phần chính, đầu tiên là danh sách đầy đủ và sau đó là giải thích từng dòng. Tổng. py

import ctypes

_sum = ctypes.CDLL('libsum.so')
_sum.our_function.argtypes = (ctypes.c_int, ctypes.POINTER(ctypes.c_int))

def our_function(numbers):
    global _sum
    num_numbers = len(numbers)
    array_type = ctypes.c_int * num_numbers
    result = _sum.our_function(ctypes.c_int(num_numbers), array_type(*numbers))
    return int(result)

Chúng tôi nhập mô-đun ctypes trong dòng đầu tiên, sau đó thư viện dùng chung được tải (thông thường bạn sẽ muốn bao gồm kiểm tra hệ điều hành tại đây và thay vào đó đặt tên cho một dll, nếu chạy trên windows). Nó được lưu trữ trong biến _sum và các hàm trong thư viện này có thể được truy cập với tư cách thành viên (e. g. _Tổng. our_functions). Trong dòng tiếp theo, chúng tôi đặt các loại đối số cho chức năng. Hàm ctypes. POINTER tạo một kiểu dữ liệu con trỏ, vì vậy ctypes. CON TRỎ(ctypes. c_int) là kiểu dữ liệu ctypes cho int *. Khi sử dụng các hàm, ctypes sẽ kiểm tra xem các đối số có phù hợp với các loại này không

Định nghĩa hàm thực theo sau trong các dòng tiếp theo. Chúng tôi không cần đối số num_numbers vì độ dài của chuỗi Python có thể được truy vấn. Đây là điều đầu tiên chúng ta làm trong hàm (sau khi nhanh chóng nói rằng _sum là toàn cục để giúp trình thông dịch tìm thấy nó). Tiếp theo, chúng ta cần xác định kiểu mảng ctypes, tương thích nhị phân với những gì thư viện mong đợi. Điều này có thể được thực hiện bằng cách 'nhân' loại ctypes. c_int với một số nguyên. Để tạo một mảng với kiểu này, chúng ta sử dụng

array_type(*numbers)

Loại mong đợi mỗi phần tử mảng làm đối số, vì vậy chúng tôi sử dụng ký hiệu dấu hoa thị với các số. Kết quả có kiểu ctypes. c_int và chúng tôi muốn trả về một số nguyên python, do đó int(result) được trả về

Sử dụng mô-đun trình bao bọc này khá đơn giản

import sum
print sum.our_function([1,2,-3,4,-5,6])

Chức năng gọi lại

ctypes sử dụng thư viện tạo các hàm tuân theo quy ước gọi nền tảng phụ thuộc vào trong thời gian chạy. Nhờ đó, cũng có thể bọc một hàm Python theo cách mà nó có thể gọi được từ C. Khi xử lý các API bao gồm các sự kiện (e. g. một thư viện GUI), điều này rất hữu ích, vì nó cho phép chúng tôi sử dụng các hàm Python làm hàm gọi lại C

Để làm điều này, chúng ta cần tạo một kiểu hàm với phương thức ctypes. CFUNCTYPE

callback_type = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_float, ctypes.c_float)

Điều này tạo ra một loại hàm tương đương với loại C sau

typedef int (*callback_type)(float, float);

Tiếp theo, chúng tôi tạo một thể hiện của loại này với hàm Python của chúng tôi

def greater_than(a,b):
    if a > b:
        return 1
    else:
        return 0
callback_func = callback_type(greater_than)

Callback_func này hiện có thể được sử dụng làm đối số bình thường cho các hàm C. Điều rất quan trọng là đảm bảo rằng bạn giữ một tham chiếu đến loại này miễn là bất kỳ thư viện C nào cũng có thể gọi nó. Nếu nó bị xóa bởi trình thu gom rác, việc gọi nó từ C có thể gây ra lỗi phân tách hoặc thậm chí có thể diễn giải bộ nhớ ngẫu nhiên thành ngôn ngữ máy