ctypes - Người mới bắt đầu


100

Tôi có nhiệm vụ "gói" thư viện ac vào một lớp python. Các tài liệu rất mơ hồ về vấn đề này. Có vẻ như họ mong đợi chỉ những người dùng python nâng cao mới triển khai ctypes. Tôi là người mới bắt đầu chơi python và cần giúp đỡ.

Một số trợ giúp từng bước sẽ là tuyệt vời.

Vì vậy, tôi có thư viện c của tôi. Tôi làm gì? Tôi đặt những tập tin nào ở đâu? Làm cách nào để nhập thư viện? Tôi đọc rằng có thể có một cách để "tự động bọc" sang Python?

(Nhân tiện, tôi đã thực hiện hướng dẫn ctypes trên python.net và nó không hoạt động. Có nghĩa là tôi nghĩ rằng họ đang cho rằng tôi có thể điền vào các bước còn lại.

Trên thực tế, đây là lỗi tôi gặp phải với mã của họ:

File "importtest.py", line 1
   >>> from ctypes import *
   SyntaxError: invalid syntax

Tôi thực sự có thể sử dụng một số trợ giúp từng bước về việc này! Cảm ơn ~


10
Bạn có >>> trong importtest.py không? Khi mọi người đăng mã có >>> trên mỗi dòng, nó có nghĩa là nó đang được chạy trong trình bao tương tác. Để chạy nó từ một tệp, hãy xóa >>> (đó là 3 dấu> và một khoảng trắng) ở bất cứ nơi nào nó xuất hiện.
Chinmay Kanchi

4
Đừng gõ >>>s. Chúng được in bởi shell tương tác và nên được loại bỏ khỏi tệp nguồn của bạn.
nmichaels

8
>>>trong tệp .py! RỒI! Chưa bao giờ thấy điều đó trước đây!
David Heffernan

3
Thành thật mà nói, hãy học một chút Python (ít nhất là một chút) trước khi bạn bắt đầu làm rối với ctypes. Bạn sẽ không bao giờ tìm thấy một hướng dẫn về ctypes giả định rằng bạn không biết Python cơ bản.
Chinmay Kanchi

3
@spentak: nếu bạn yêu cầu giúp đỡ, cung cấp thông tin đầy đủ. Ít nhất hãy cho chúng tôi xem phiên bản mã cuối cùng mà bạn đang nói đến. Ví dụ: trên "dòng 3" là gì?
Francesco

Câu trả lời:


228

Đây là hướng dẫn về ctypes nhanh chóng và bẩn thỉu.

Đầu tiên, viết thư viện C của bạn. Đây là một ví dụ đơn giản về Hello world:

testlib.c

#include <stdio.h>

void myprint(void);

void myprint()
{
    printf("hello world\n");
}

Bây giờ biên dịch nó dưới dạng thư viện được chia sẻ ( bản sửa lỗi mac được tìm thấy ở đây ):

$ gcc -shared -Wl,-soname,testlib -o testlib.so -fPIC testlib.c

# or... for Mac OS X 
$ gcc -shared -Wl,-install_name,testlib.so -o testlib.so -fPIC testlib.c

Sau đó, viết một trình bao bọc bằng ctypes:

testlibwrapper.py

import ctypes

testlib = ctypes.CDLL('/full/path/to/testlib.so')
testlib.myprint()

Bây giờ thực thi nó:

$ python testlibwrapper.py

Và bạn sẽ thấy đầu ra

Hello world
$

Nếu bạn đã có thư viện trong đầu, bạn có thể bỏ qua phần không phải python của hướng dẫn. Đảm bảo ctypes có thể tìm thấy thư viện bằng cách đặt nó vào /usr/libhoặc một thư mục tiêu chuẩn khác. Nếu bạn làm điều này, bạn không cần chỉ định đường dẫn đầy đủ khi viết trình bao bọc. Nếu bạn chọn không làm điều này, bạn phải cung cấp đường dẫn đầy đủ của thư viện khi gọictypes.CDLL() .

Đây không phải là nơi dành cho hướng dẫn toàn diện hơn, nhưng nếu bạn yêu cầu trợ giúp về các vấn đề cụ thể trên trang web này, tôi chắc chắn rằng cộng đồng sẽ giúp bạn.

Tái bút: Tôi cho rằng bạn đang sử dụng Linux vì bạn đã sử dụng ctypes.CDLL('libc.so.6'). Nếu bạn đang sử dụng hệ điều hành khác, mọi thứ có thể thay đổi một chút (hoặc khá nhiều).


1
@ Chinmay: Tôi có thể có một mã tương tự cho Windows và thay vì C, bạn có thể vui lòng cung cấp một ví dụ trực quan về c ++ không? Tôi có thể tải thư viện của mình nhưng tôi không thể truy cập các chức năng của mình từ tệp .dll. Nó luôn luôn thông báo "không tìm thấy hàm 'xyz'". Bạn có thể gợi ý cho tôi một cách giải quyết vấn đề này không? Chúc mừng.
Neophile

Tôi không biết nhiều về sự phát triển của Windows, nhưng có vẻ như Windows làm được điều gì đó khó hiểu, có lẽ nó sử dụng một quy ước gọi khác? Có lẽ bạn có thể tìm kiếm việc xuất các hàm C ++ của mình bằng cách sử dụng "extern C"?
Chinmay Kanchi

Vâng, tôi đã làm điều đó nhưng không có may mắn cho đến nay.
Neophile

6
Cảm ơn đã dễ dàng để làm theo hướng dẫn mà chương trình các chức năng cơ bản của CType
okysabeni

1
nhanh chóng và dơ bẩn luôn là những hướng dẫn tốt nhất
lurscher

55

Câu trả lời của Chinmay Kanchi là tuyệt vời nhưng tôi muốn có một ví dụ về một hàm truyền và trả về một biến / mảng cho mã C ++. Tôi mặc dù tôi sẽ đưa nó vào đây trong trường hợp nó hữu ích cho người khác.

Chuyển và trả về một số nguyên

Mã C ++ cho một hàm nhận một số nguyên và thêm một số nguyên vào giá trị trả về,

extern "C" int add_one(int i)
{
    return i+1;
}

Đã lưu dưới dạng tệp test.cpp, hãy lưu ý chữ "C" bên ngoài bắt buộc (có thể loại bỏ điều này đối với mã C). Điều này được biên dịch bằng cách sử dụng g ++, với các đối số tương tự như câu trả lời Chinmay Kanchi,

g++ -shared -o testlib.so -fPIC test.cpp

Mã Python sử dụng load_librarytừ numpy.ctypeslibgiả định đường dẫn đến thư viện được chia sẻ trong cùng một thư mục với tập lệnh Python,

import numpy.ctypeslib as ctl
import ctypes

libname = 'testlib.so'
libdir = './'
lib=ctl.load_library(libname, libdir)

py_add_one = lib.add_one
py_add_one.argtypes = [ctypes.c_int]
value = 5
results = py_add_one(value)
print(results)

Điều này in 6 như mong đợi.

Truyền và in một mảng

Bạn cũng có thể chuyển các mảng như sau, đối với mã C để in phần tử của mảng,

extern "C" void print_array(double* array, int N)
{
    for (int i=0; i<N; i++) 
        cout << i << " " << array[i] << endl;
}

được biên dịch như trước và được nhập theo cùng một cách. Mã Python bổ sung để sử dụng hàm này sau đó sẽ là,

import numpy as np

py_print_array = lib.print_array
py_print_array.argtypes = [ctl.ndpointer(np.float64, 
                                         flags='aligned, c_contiguous'), 
                           ctypes.c_int]
A = np.array([1.4,2.6,3.0], dtype=np.float64)
py_print_array(A, 3)

trong đó chúng tôi chỉ định mảng, đối số đầu tiên print_array, dưới dạng con trỏ tới một mảng Numpy có 64 bit được căn chỉnh, liên tục và đối số thứ hai là một số nguyên cho mã C biết số phần tử trong mảng Numpy. Điều này sau đó được in bởi mã C như sau,

1.4
2.6
3.0

5
Đây là một câu trả lời bổ sung tuyệt vời - thật tiếc là không thể có hai câu trả lời được đánh dấu :(
jtlz2 Ngày

Không chắc nếu nó quá rõ ràng, nhưng có một lỗi trong mã. Nó bị thiếu import numpy as np. Nếu không, nó không thể tìm thấy np.float64và những thứ khác.
Ben

@Ben, vị trí tốt, đã thêm nó vào
Ed Smith

11

Thứ nhất: >>>Mã bạn thấy trong các ví dụ về python là một cách để chỉ ra rằng đó là mã Python. Nó được sử dụng để tách mã Python khỏi đầu ra. Như thế này:

>>> 4+5
9

Ở đây chúng ta thấy rằng dòng bắt đầu bằng >>>mã Python và 9 là kết quả của nó. Đây chính xác là nó trông như thế nào nếu bạn bắt đầu một trình thông dịch Python, đó là lý do tại sao nó được thực hiện như vậy.

Bạn không bao giờ nhập >>>một phần vào một .pytệp.

Điều đó xử lý lỗi cú pháp của bạn.

Thứ hai, ctypes chỉ là một trong nhiều cách gói các thư viện Python. Các cách khác là SWIG , sẽ xem xét thư viện Python của bạn và tạo mô-đun mở rộng Python C để hiển thị API C. Một cách khác là sử dụng Cython .

Tất cả chúng đều có lợi ích và hạn chế.

SWIG sẽ chỉ hiển thị API C của bạn với Python. Điều đó có nghĩa là bạn không nhận được bất kỳ đối tượng hoặc bất kỳ thứ gì, bạn sẽ phải tạo một tệp Python riêng để làm điều đó. Tuy nhiên, việc có một mô-đun gọi là say "wowza" và một mô-đun SWIG được gọi là "_wowza" là một trình bao bọc xung quanh C API. Đây là một cách làm tốt và dễ dàng.

Cython tạo tệp C-Extension. Nó có lợi ích là tất cả mã Python bạn viết đều được tạo thành C, vì vậy các đối tượng bạn viết cũng bằng C, đây có thể là một sự cải thiện hiệu suất. Nhưng bạn sẽ phải học cách nó giao tiếp với C vì vậy sẽ tốn thêm một chút công sức để học cách sử dụng nó.

ctypes có lợi ích là không có mã C để biên dịch, vì vậy rất hay khi sử dụng để gói các thư viện tiêu chuẩn do người khác viết và đã tồn tại trong các phiên bản nhị phân cho Windows và OS X.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.