Bao bọc một thư viện C trong Python: C, Cython hoặc ctypes?


284

Tôi muốn gọi một thư viện C từ một ứng dụng Python. Tôi không muốn bọc toàn bộ API, chỉ các hàm và kiểu dữ liệu có liên quan đến trường hợp của tôi. Theo tôi thấy, tôi có ba lựa chọn:

  1. Tạo một mô-đun mở rộng thực tế trong C. Có thể là quá mức cần thiết và tôi cũng muốn tránh chi phí viết bài mở rộng.
  2. Sử dụng Cython để hiển thị các phần có liên quan từ thư viện C sang Python.
  3. Làm toàn bộ trong Python, sử dụng ctypesđể giao tiếp với thư viện bên ngoài.

Tôi không chắc liệu 2) hay 3) là lựa chọn tốt hơn. Ưu điểm của 3) là ctypesmột phần của thư viện chuẩn và mã kết quả sẽ là Python thuần túy - mặc dù tôi không chắc lợi thế đó thực sự lớn đến mức nào.

Có nhiều lợi thế / bất lợi với một trong hai sự lựa chọn? Cách tiếp cận nào bạn đề nghị?


Chỉnh sửa: Cảm ơn tất cả các câu trả lời của bạn, họ cung cấp một nguồn tài nguyên tốt cho bất cứ ai muốn làm điều gì đó tương tự. Tất nhiên, quyết định vẫn được đưa ra cho một trường hợp duy nhất mà không có câu trả lời "Đây là điều đúng". Đối với trường hợp của riêng tôi, có lẽ tôi sẽ đi với ctypes, nhưng tôi cũng mong muốn được thử Cython trong một số dự án khác.

Với việc không có câu trả lời đúng duy nhất, việc chấp nhận một câu trả lời có phần tùy tiện; Tôi đã chọn câu trả lời của FogleBird vì nó cung cấp một số thông tin chi tiết tốt về ctypes và hiện tại nó cũng là câu trả lời được bình chọn cao nhất. Tuy nhiên, tôi đề nghị đọc tất cả các câu trả lời để có cái nhìn tổng quan tốt.

Cảm ơn một lần nữa.


3
Ở một mức độ nào đó, ứng dụng cụ thể liên quan (những gì thư viện làm) có thể ảnh hưởng đến sự lựa chọn phương pháp tiếp cận. Chúng tôi đã sử dụng ctypes khá thành công để nói chuyện với các DLL do nhà cung cấp cung cấp cho nhiều phần cứng khác nhau (ví dụ: máy hiện sóng) nhưng trước tiên tôi không nhất thiết phải chọn ctypes để nói chuyện với thư viện xử lý số, vì có thêm chi phí so với Cython hoặc SWIG.
Peter Hansen

1
Bây giờ bạn có những gì bạn đang tìm kiếm. Bốn câu trả lời khác nhau. (Ai đó cũng tìm thấy SWIG). Điều đó có nghĩa là bây giờ bạn có 4 lựa chọn thay vì 3.
Luka Rahne

@ralu Đó cũng là điều tôi cảm thấy khó khăn :-) Nhưng nghiêm túc, tôi không mong đợi (hoặc muốn) một bảng pro / con hoặc một câu trả lời duy nhất có nội dung "Đây là những gì bạn cần làm". Bất kỳ câu hỏi về việc ra quyết định được trả lời tốt nhất với "người hâm mộ" của mỗi lựa chọn có thể đưa ra lý do của họ. Cộng đồng bỏ phiếu sau đó thực hiện phần của mình, cũng như công việc của riêng tôi (xem xét các đối số, áp dụng chúng cho trường hợp của tôi, đọc các nguồn được cung cấp, v.v.). Câu chuyện dài: Có một số câu trả lời tốt ở đây.
balpha

Vì vậy, cách bạn sẽ đi với? :)
FogleBird

1
Theo như tôi biết (xin vui lòng sửa lỗi cho tôi nếu tôi sai), Cython là một nhánh của Pyrex với sự phát triển hơn nữa, khiến Pyrex trở nên lỗi thời.
balpha

Câu trả lời:


115

ctypes là sự lựa chọn tốt nhất của bạn để hoàn thành nó một cách nhanh chóng và thật vui khi làm việc với bạn khi bạn vẫn đang viết Python!

Gần đây tôi đã gói trình điều khiển FTDI để giao tiếp với chip USB bằng cách sử dụng ctypes và điều đó thật tuyệt. Tôi đã hoàn thành tất cả và làm việc trong ít hơn một ngày làm việc. (Tôi chỉ thực hiện các chức năng chúng tôi cần, khoảng 15 chức năng).

Chúng tôi trước đây đã sử dụng một mô-đun của bên thứ ba, PyUSB , cho cùng một mục đích. PyUSB là một mô-đun mở rộng C / Python thực tế. Nhưng PyUSB đã không phát hành GIL khi chặn đọc / ghi, điều này gây ra vấn đề cho chúng tôi. Vì vậy, tôi đã viết mô-đun riêng của chúng tôi bằng cách sử dụng ctypes, nó giải phóng GIL khi gọi các hàm gốc.

Một điều cần lưu ý là ctypes sẽ không biết về các #definehằng số và nội dung trong thư viện bạn đang sử dụng, chỉ có các hàm, vì vậy bạn sẽ phải xác định lại các hằng số đó trong mã của riêng bạn.

Đây là một ví dụ về cách mã cuối cùng trông như thế nào (rất nhiều người bị bắn ra, chỉ cố gắng cho bạn thấy ý chính của nó):

from ctypes import *

d2xx = WinDLL('ftd2xx')

OK = 0
INVALID_HANDLE = 1
DEVICE_NOT_FOUND = 2
DEVICE_NOT_OPENED = 3

...

def openEx(serial):
    serial = create_string_buffer(serial)
    handle = c_int()
    if d2xx.FT_OpenEx(serial, OPEN_BY_SERIAL_NUMBER, byref(handle)) == OK:
        return Handle(handle.value)
    raise D2XXException

class Handle(object):
    def __init__(self, handle):
        self.handle = handle
    ...
    def read(self, bytes):
        buffer = create_string_buffer(bytes)
        count = c_int()
        if d2xx.FT_Read(self.handle, buffer, bytes, byref(count)) == OK:
            return buffer.raw[:count.value]
        raise D2XXException
    def write(self, data):
        buffer = create_string_buffer(data)
        count = c_int()
        bytes = len(data)
        if d2xx.FT_Write(self.handle, buffer, bytes, byref(count)) == OK:
            return count.value
        raise D2XXException

Ai đó đã làm một số điểm chuẩn trên các tùy chọn khác nhau.

Tôi có thể do dự hơn nếu tôi phải bọc một thư viện C ++ với rất nhiều lớp / mẫu / v.v. Nhưng ctypes hoạt động tốt với các cấu trúc và thậm chí có thể gọi lại vào Python.


5
Tham gia những lời khen ngợi cho ctypes, nhưng hãy chú ý một vấn đề (không có giấy tờ): ctypes không hỗ trợ việc giả mạo. Nếu bạn rẽ nhánh từ một quá trình sử dụng ctypes và cả hai quá trình cha và con tiếp tục sử dụng ctypes, bạn sẽ vấp phải một lỗi khó chịu xảy ra với ctypes sử dụng bộ nhớ dùng chung.
Oren Shemesh

1
@OrenShemesh Có đọc thêm về vấn đề này bạn có thể chỉ cho tôi không? Tôi nghĩ rằng tôi có thể an toàn với một dự án mà tôi hiện đang thực hiện, vì tôi tin rằng chỉ có quy trình cha mẹ sử dụng ctypes(cho pyinotify), nhưng tôi muốn hiểu vấn đề kỹ hơn.
zigg

Đoạn văn này giúp tôi rất nhiều One thing to note is that ctypes won't know about #define constants and stuff in the library you're using, only the functions, so you'll have to redefine those constants in your own code.Vì vậy, tôi phải xác định các hằng số có trong winioctl.h....
swdev

hiệu suất như thế nào? ctypeschậm hơn nhiều so với phần mở rộng c vì nút cổ chai là giao diện từ Python sang C
TomSawyer

154

Cảnh báo: một ý kiến ​​của nhà phát triển cốt lõi Cython phía trước.

Tôi hầu như luôn luôn giới thiệu Cython trên ctypes. Lý do là nó có đường dẫn nâng cấp mượt mà hơn nhiều. Nếu bạn sử dụng ctypes, ban đầu nhiều thứ sẽ đơn giản và thật tuyệt khi viết mã FFI của bạn bằng Python đơn giản, không cần biên dịch, xây dựng các phụ thuộc và tất cả những thứ đó. Tuy nhiên, tại một số điểm, bạn gần như chắc chắn sẽ thấy rằng bạn phải gọi vào thư viện C của mình rất nhiều, trong một vòng lặp hoặc trong một chuỗi các cuộc gọi phụ thuộc lẫn nhau dài hơn, và bạn muốn tăng tốc độ đó. Đó là điểm mà bạn sẽ nhận thấy rằng bạn không thể làm điều đó với ctypes. Hoặc, khi bạn cần các hàm gọi lại và bạn thấy rằng mã gọi lại Python của bạn trở thành nút cổ chai, bạn cũng muốn tăng tốc và / hoặc chuyển nó xuống C cũng vậy. Một lần nữa, bạn không thể làm điều đó với ctypes.

Với Cython, OTOH, bạn hoàn toàn tự do thực hiện gói và gọi mã mỏng hoặc dày như bạn muốn. Bạn có thể bắt đầu bằng các cuộc gọi đơn giản vào mã C của mình từ mã Python thông thường và Cython sẽ dịch chúng thành các cuộc gọi C gốc, mà không cần bất kỳ chi phí gọi bổ sung nào và với chi phí chuyển đổi cực thấp cho các tham số Python. Khi bạn nhận thấy rằng bạn cần hiệu năng cao hơn nữa tại một số điểm mà bạn đang thực hiện quá nhiều cuộc gọi đắt tiền vào thư viện C của mình, bạn có thể bắt đầu chú thích mã Python xung quanh của mình bằng các loại tĩnh và để Cython tối ưu hóa thẳng vào C cho bạn. Hoặc, bạn có thể bắt đầu viết lại các phần của mã C trong Cython để tránh các cuộc gọi và để chuyên môn hóa và thắt chặt các vòng lặp của bạn theo thuật toán. Và nếu bạn cần gọi lại nhanh, chỉ cần viết một hàm có chữ ký thích hợp và chuyển trực tiếp vào sổ đăng ký gọi lại C. Một lần nữa, không có chi phí, và nó cung cấp cho bạn hiệu suất gọi C đơn giản. Và trong trường hợp ít có khả năng là bạn thực sự không thể nhận được mã của mình đủ nhanh trong Cython, bạn vẫn có thể xem xét viết lại các phần thực sự quan trọng của nó trong C (hoặc C ++ hoặc Fortran) và gọi nó từ mã Cython của bạn một cách tự nhiên và tự nhiên. Nhưng sau đó, đây thực sự trở thành phương sách cuối cùng thay vì lựa chọn duy nhất.

Vì vậy, ctypes thật tuyệt khi làm những việc đơn giản và nhanh chóng có được thứ gì đó đang chạy. Tuy nhiên, ngay khi mọi thứ bắt đầu phát triển, rất có thể bạn sẽ đến điểm mà bạn nhận thấy rằng bạn đã sử dụng Cython tốt hơn ngay từ đầu.


4
+1 đó là những điểm tốt, cảm ơn rất nhiều! Mặc dù tôi tự hỏi nếu chỉ di chuyển các phần cổ chai sang Cython thực sự là quá nhiều chi phí. Nhưng tôi đồng ý, nếu bạn mong đợi bất kỳ loại vấn đề hiệu suất nào, bạn cũng có thể sử dụng Cython ngay từ đầu.
balpha

Điều này có còn giữ cho các lập trình viên có kinh nghiệm với cả C và Python không? Trong trường hợp đó, người ta có thể lập luận rằng Python / ctypes là lựa chọn tốt hơn, vì việc vector hóa các vòng lặp C (SIMD) đôi khi đơn giản hơn. Nhưng, ngoài điều đó ra, tôi không thể nghĩ ra bất kỳ nhược điểm nào của Cython.
Alex van Houten

Cảm ơn câu trả lời! Một điều tôi gặp rắc rối với Cython là xử lý đúng quy trình xây dựng (nhưng điều đó cũng phải làm với tôi không bao giờ viết mô-đun Python trước đây) - tôi có nên biên dịch nó trước đó không, hoặc bao gồm các tệp nguồn Cython trong sdist và các câu hỏi tương tự. Tôi đã viết một bài đăng trên blog về nó trong trường hợp bất kỳ ai có vấn đề / nghi ngờ tương tự: martinsosic.com/development/2016/02/08/ mẹo
Martinsos

Cảm ơn câu trả lời! Một nhược điểm khi tôi sử dụng Cython là quá tải toán tử không được thực hiện đầy đủ (ví dụ __radd__). Điều này đặc biệt khó chịu khi bạn lên kế hoạch cho lớp của mình tương tác với các loại dựng sẵn (ví dụ intfloat). Ngoài ra, các phương thức ma thuật trong cython chỉ là một lỗi nhỏ nói chung.
Monolith

100

Cython là một công cụ khá thú vị, rất đáng để học hỏi và gần với cú pháp Python. Nếu bạn thực hiện bất kỳ tính toán khoa học nào với Numpy, thì Cython là cách tốt nhất vì nó tích hợp với Numpy cho các hoạt động ma trận nhanh.

Cython là một siêu ngôn ngữ của Python. Bạn có thể ném bất kỳ tệp Python hợp lệ nào vào nó và nó sẽ nhổ ra một chương trình C hợp lệ. Trong trường hợp này, Cython sẽ chỉ ánh xạ các lệnh gọi Python tới API CPython bên dưới. Điều này có thể dẫn đến việc tăng tốc 50% vì mã của bạn không còn được diễn giải.

Để có được một số tối ưu hóa, bạn phải bắt đầu cho Cython biết thêm sự thật về mã của bạn, chẳng hạn như khai báo kiểu. Nếu bạn nói đủ, nó có thể biến mã thành thuần túy C. Nghĩa là, một vòng lặp for trong Python trở thành một vòng lặp for trong C. Ở đây bạn sẽ thấy tốc độ tăng nhanh. Bạn cũng có thể liên kết với các chương trình C bên ngoài tại đây.

Sử dụng mã Cython cũng cực kỳ dễ dàng. Tôi nghĩ rằng hướng dẫn làm cho nó khó khăn. Bạn thực sự chỉ cần làm:

$ cython mymodule.pyx
$ gcc [some arguments here] mymodule.c -o mymodule.so

và sau đó bạn có thể import mymoduletrong mã Python của mình và quên hoàn toàn rằng nó biên dịch thành C.

Trong mọi trường hợp, vì Cython rất dễ thiết lập và bắt đầu sử dụng, tôi khuyên bạn nên dùng thử để xem nó có phù hợp với nhu cầu của bạn không. Sẽ không lãng phí nếu hóa ra nó không phải là công cụ bạn đang tìm kiếm.


1
Không vấn đề gì. Điều thú vị về Cython là bạn chỉ có thể học những gì bạn cần. Nếu bạn chỉ muốn một cải tiến khiêm tốn, tất cả những gì bạn phải làm là biên dịch các tệp Python của bạn và bạn đã hoàn tất.
carl

18
"Bạn có thể ném bất kỳ tệp Python hợp lệ nào vào nó và nó sẽ nhổ ra một chương trình C hợp lệ." <- Không hoàn toàn, có một số hạn chế: docs.cython.org/src/userguide/limemony.html Có thể không phải là vấn đề đối với hầu hết các trường hợp sử dụng, nhưng chỉ muốn được hoàn thành.
Randy Syring

7
Các vấn đề đang trở nên ít hơn với mỗi bản phát hành, đến mức mà trang đó hiện nói rằng "hầu hết các vấn đề đã được giải quyết trong 0.15".
Henry Gomersall

3
Để thêm vào, có một cách dễ dàng hơn để nhập mã cython: viết mã cython của bạn dưới dạng một mymod.pyxmô-đun và sau đó thực hiện import pyximport; pyximport.install(); import mymodvà quá trình biên dịch xảy ra đằng sau hậu trường.
Kaushik Ghose

3
@kaushik Thậm chí đơn giản hơn là pypi.python.org/pypi/runcython . Chỉ cần sử dụng runcython mymodule.pyx. Và không giống như pySenseport, bạn có thể sử dụng nó cho các nhiệm vụ liên kết đòi hỏi khắt khe hơn. Chỉ có một điều lưu ý là tôi là người đã viết 20 dòng bash cho nó và có thể bị sai lệch.
RussellStewart

42

Để gọi một thư viện C từ một ứng dụng Python, cũng có cffi , đây là một thay thế mới cho ctypes . Nó mang lại một cái nhìn mới mẻ cho FFI:

  • nó xử lý vấn đề một cách hấp dẫn, sạch sẽ (trái ngược với ctypes )
  • không yêu cầu viết mã không phải Python (như trong SWIG, Cython , ...)

chắc chắn là cách để đi cho gói , như OP muốn. cython nghe có vẻ tuyệt vời khi tự viết chúng cho các vòng lặp nóng, nhưng đối với giao diện, cffi chỉ đơn giản là một bản nâng cấp thẳng từ ctypes.
cừu bay

21

Tôi sẽ ném một cái khác ra khỏi đó: SWIG

Thật dễ dàng để học, làm rất nhiều thứ đúng và hỗ trợ nhiều ngôn ngữ hơn vì vậy thời gian học nó có thể khá hữu ích.

Nếu bạn sử dụng SWIG, bạn đang tạo một mô-đun mở rộng python mới, nhưng với SWIG thực hiện hầu hết các công việc nặng nhọc cho bạn.


18

Cá nhân, tôi sẽ viết một mô-đun mở rộng bằng C. Đừng bị đe dọa bởi các tiện ích mở rộng Python C - chúng không khó để viết. Các tài liệu rất rõ ràng và hữu ích. Khi tôi lần đầu tiên viết một phần mở rộng C bằng Python, tôi nghĩ rằng tôi đã mất khoảng một giờ để tìm ra cách viết một phần mở rộng - không có nhiều thời gian.


Bao bọc một thư viện C. Bạn thực sự có thể tìm thấy mã ở đây: github.com/mdippery/lehmer
mipadi

1
@forivall: Mã không thực sự hữu ích cho lắm, và có những trình tạo số ngẫu nhiên tốt hơn ngoài kia. Tôi chỉ có một bản sao lưu trên máy tính của tôi.
mipadi

2
Đã đồng ý. C-API của Python không đáng sợ như vẻ ngoài của nó (giả sử bạn biết C). Tuy nhiên, không giống như python và kho thư viện, tài nguyên và nhà phát triển của nó, khi viết các phần mở rộng trong C về cơ bản bạn chỉ có một mình. Có lẽ nhược điểm duy nhất của nó (khác với những thứ thường đi kèm với viết bằng C).
Noob Saibot

1
@mipadi: tốt, nhưng chúng khác nhau giữa Python 2.x và 3.x, vì vậy sẽ thuận tiện hơn khi sử dụng Cython để viết tiện ích mở rộng của bạn, yêu cầu Cython tìm ra tất cả các chi tiết và sau đó biên dịch mã C được tạo cho Python 2.x hoặc 3.x khi cần thiết.
0xC0000022L

2
@mipadi Có vẻ như liên kết github đã chết và dường như không có sẵn trên archive.org, bạn có bản sao lưu không?
jrh

11

ctypes thật tuyệt vời khi bạn đã có một blob thư viện được biên dịch để xử lý (chẳng hạn như các thư viện hệ điều hành). Tuy nhiên, chi phí cuộc gọi rất nghiêm trọng, vì vậy nếu bạn sẽ thực hiện nhiều cuộc gọi vào thư viện và dù sao bạn cũng sẽ viết mã C (hoặc ít nhất là biên dịch nó), tôi sẽ nói là hãy đi cython . Nó không phải là nhiều công việc hơn, và nó sẽ nhanh hơn và nhiều pythonic hơn để sử dụng tệp pyd kết quả.

Cá nhân tôi có xu hướng sử dụng cython để tăng tốc nhanh mã python (vòng lặp và so sánh số nguyên là hai lĩnh vực mà cython đặc biệt tỏa sáng) và khi có thêm một số mã / gói thư viện khác có liên quan, tôi sẽ chuyển sang Boost.Python . Boost.Python có thể rất khó để thiết lập, nhưng một khi bạn đã làm việc xong, nó sẽ giúp việc gói mã C / C ++ trở nên đơn giản.

cython cũng rất tuyệt trong việc gói numpy (mà tôi đã học được từ quá trình tố tụng SciPy 2009 ), nhưng tôi đã không sử dụng numpy, vì vậy tôi không thể nhận xét về điều đó.


11

Nếu bạn đã có một thư viện với API được xác định, tôi nghĩ ctypeslà tùy chọn tốt nhất, vì bạn chỉ phải thực hiện một chút khởi tạo và sau đó ít nhiều gọi thư viện theo cách bạn đã sử dụng.

Tôi nghĩ Cython hoặc tạo một mô-đun mở rộng trong C (không khó lắm) sẽ hữu ích hơn khi bạn cần mã mới, ví dụ như gọi thư viện đó và thực hiện một số tác vụ phức tạp, tốn thời gian và sau đó chuyển kết quả cho Python.

Một cách tiếp cận khác, đối với các chương trình đơn giản, là trực tiếp thực hiện một quy trình khác (được biên dịch bên ngoài), đưa kết quả ra đầu ra tiêu chuẩn và gọi nó bằng mô đun quy trình con. Đôi khi đó là cách tiếp cận dễ dàng nhất.

Ví dụ: nếu bạn tạo một chương trình C console hoạt động nhiều hơn hoặc ít hơn theo cách đó

$miCcode 10
Result: 12345678

Bạn có thể gọi nó từ Python

>>> import subprocess
>>> p = subprocess.Popen(['miCcode', '10'], shell=True, stdout=subprocess.PIPE)
>>> std_out, std_err = p.communicate()
>>> print std_out
Result: 12345678

Với một chuỗi định dạng nhỏ, bạn có thể nhận kết quả theo bất kỳ cách nào bạn muốn. Bạn cũng có thể chụp đầu ra lỗi tiêu chuẩn, vì vậy nó khá linh hoạt.


Mặc dù không có gì sai với câu trả lời này, mọi người nên thận trọng nếu mã được mở để truy cập bởi người khác vì việc gọi quy trình con có shell=Truethể dễ dàng dẫn đến một kiểu khai thác khi người dùng thực sự có được vỏ. Thật tốt khi nhà phát triển là người dùng duy nhất, nhưng trên thế giới có cả đống những điều khó chịu chỉ chờ đợi một cái gì đó như thế này.
Bến

7

Có một vấn đề khiến tôi sử dụng ctypes chứ không phải cython và không được đề cập trong các câu trả lời khác.

Sử dụng ctypes kết quả không phụ thuộc vào trình biên dịch bạn đang sử dụng. Bạn có thể viết thư viện bằng cách sử dụng nhiều hơn hoặc ít hơn bất kỳ ngôn ngữ nào có thể được biên dịch sang thư viện dùng chung. Nó không quan trọng nhiều, hệ thống nào, ngôn ngữ nào và trình biên dịch nào. Cython, tuy nhiên, bị giới hạn bởi cơ sở hạ tầng. Ví dụ, nếu bạn muốn sử dụng trình biên dịch intel trên windows, sẽ khó khăn hơn nhiều để khiến cython hoạt động: bạn nên "giải thích" trình biên dịch với cython, biên dịch lại một cái gì đó với trình biên dịch chính xác này, v.v ... Điều này hạn chế đáng kể tính di động.


4

Nếu bạn đang nhắm mục tiêu Windows và chọn bọc một số thư viện C ++ độc quyền, thì bạn có thể sớm phát hiện ra rằng các phiên bản khác nhau của msvcrt***.dll(Visual C ++ Runtime) không tương thích một chút.

Điều này có nghĩa là bạn không thể sử dụng Cythonvì kết quả wrapper.pydđược liên kết với msvcr90.dll (Python 2.7) hoặc msvcr100.dll (Python 3.x) . Nếu thư viện mà bạn đang gói được liên kết với các phiên bản thời gian chạy khác nhau, thì bạn đã hết may mắn.

Sau đó, để mọi thứ hoạt động, bạn cần tạo trình bao C cho thư viện C ++, liên kết trình bao bọc đó với phiên bản tương tự msvcrt***.dllnhư thư viện C ++ của bạn. Và sau đó sử dụng ctypesđể tải dll trình bao bọc bằng tay của bạn một cách linh hoạt trong thời gian chạy.

Vì vậy, có rất nhiều chi tiết nhỏ, được mô tả rất chi tiết trong bài viết sau:

"Thư viện bản địa đẹp (bằng Python) ": http://lucumr.pocoo.org/2013/8/18/beautificent-native-lologists/


Bài viết đó không liên quan gì đến các vấn đề bạn đưa ra với khả năng tương thích của trình biên dịch Microsoft. Bắt các tiện ích mở rộng Cython hoạt động trên Windows thực sự không khó lắm. Tôi đã có thể sử dụng MinGW cho mọi thứ. Một phân phối Python tốt giúp mặc dù.
IanH

2
+1 để đề cập đến một vấn đề có thể xảy ra trên windows (hiện tại tôi cũng đang gặp phải ...). @IanH nói chung là ít về các cửa sổ, nhưng thật rắc rối nếu bạn bị mắc kẹt với một bên thứ ba đã cho không phù hợp với phân phối trăn của bạn.
sebastian


2

Tôi biết đây là một câu hỏi cũ nhưng điều này xuất hiện trên google khi bạn tìm kiếm những thứ như thế ctypes vs cython, và hầu hết các câu trả lời ở đây được viết bởi những người thành thạo cythonhoặc ccó thể không phản ánh thời gian thực tế bạn cần đầu tư để tìm hiểu những điều đó để thực hiện giải pháp của bạn. Tôi là một người mới bắt đầu hoàn thành trong cả hai. Tôi chưa bao giờ chạm vào cythontrước đây, và có rất ít kinh nghiệm trên c/c++.

Trong hai ngày qua, tôi đã tìm cách để ủy thác một phần hiệu năng nặng của mã của tôi cho một mức độ thấp hơn python. Tôi đã triển khai mã của mình cả trong ctypesCython, về cơ bản bao gồm hai hàm đơn giản.

Tôi đã có một danh sách chuỗi lớn cần xử lý. Thông báo liststring. Cả hai loại không tương ứng hoàn hảo với các loại trong c, bởi vì các chuỗi python theo mặc định là unicode và các cchuỗi thì không. Danh sách trong python chỉ đơn giản là mảng KHÔNG của c.

Đây là bản án của tôi. Sử dụng cython. Nó tích hợp trôi chảy hơn với python và nói chung là dễ dàng hơn để làm việc. Khi có sự cố xảy ra, ctypeschỉ cần ném segfault cho bạn, ít nhất cythonsẽ cung cấp cho bạn các cảnh báo biên dịch với dấu vết ngăn xếp bất cứ khi nào có thể và bạn có thể dễ dàng trả về một đối tượng python hợp lệ cython.

Đây là một tài khoản chi tiết về thời gian tôi cần để đầu tư vào cả hai để thực hiện cùng một chức năng. Tôi đã làm rất ít lập trình C / C ++:

  • Công ty:

    • Khoảng 2h về việc nghiên cứu cách chuyển đổi danh sách các chuỗi unicode của tôi sang loại tương thích ac.
    • Khoảng một giờ về cách trả về một chuỗi đúng từ hàm ac. Ở đây tôi thực sự đã cung cấp giải pháp của riêng mình cho SO sau khi tôi đã viết các hàm.
    • Khoảng nửa giờ để viết mã trong c, biên dịch nó vào một thư viện động.
    • 10 phút để viết mã kiểm tra trong python để kiểm tra xem cmã có hoạt động không.
    • Khoảng một giờ thực hiện một số thử nghiệm và sắp xếp lại cmã.
    • Sau đó, tôi cắm cmã vào cơ sở mã thực tế và thấy rằng ctypesnó không chơi tốt với multiprocessingmô-đun vì trình xử lý của nó không thể chọn theo mặc định.
    • Khoảng 20 phút tôi sắp xếp lại mã của mình để không sử dụng multiprocessingmô-đun và thử lại.
    • Sau đó, hàm thứ hai trong cmã của tôi tạo ra segfaults trong cơ sở mã của tôi mặc dù nó đã vượt qua mã thử nghiệm của tôi. Chà, đây có lẽ là lỗi của tôi vì đã không kiểm tra tốt với các trường hợp cạnh, tôi đang tìm kiếm một giải pháp nhanh chóng.
    • Trong khoảng 40 phút, tôi đã cố gắng xác định nguyên nhân có thể của các sai lệch này.
    • Tôi chia các chức năng của mình thành hai thư viện và thử lại. Vẫn có segfaults cho chức năng thứ hai của tôi.
    • Tôi quyết định từ bỏ chức năng thứ hai và chỉ sử dụng chức năng đầu tiên của cmã và ở lần lặp thứ hai hoặc thứ ba của vòng lặp python sử dụng nó, tôi đã UnicodeErrorkhông giải mã được một byte tại vị trí nào đó mặc dù tôi đã mã hóa và giải mã mọi thứ rõ ràng

Tại thời điểm này, tôi quyết định tìm kiếm một giải pháp thay thế và quyết định xem xét cython:

  • Con trăn
    • 10 phút đọc cython xin chào thế giới .
    • 15 phút kiểm tra SO về cách sử dụng cython setuptoolsthay vì distutils.
    • 10 phút đọc trên các loại cython và các loại python. Tôi đã học được rằng tôi có thể sử dụng hầu hết các loại python dựng sẵn để gõ tĩnh.
    • 15 phút ghi lại mã python của tôi với các loại cython.
    • 10 phút sửa đổi của tôi setup.py để sử dụng mô-đun được biên dịch trong cơ sở mã của tôi.
    • Cắm mô-đun trực tiếp vào multiprocessingphiên bản của cơ sở mã. Nó hoạt động.

Đối với hồ sơ, tôi tất nhiên, không đo lường thời gian chính xác của khoản đầu tư của tôi. Rất có thể là trường hợp nhận thức của tôi về thời gian là một chút chú ý do nỗ lực tinh thần cần thiết trong khi tôi đang làm việc với ctypes. Nhưng nó sẽ truyền đạt cảm giác đối phó cythonctypes

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.