המודול ctypes מאפשר הפעלה קלה של קוד C מתוך תוכנית פייתון, ונועד בדיוק למקרים בהם יש לכם כבר ספריה עובדת ב C ואתם רוצים לעטוף אותה בממשק נוח יותר למתכנתי פייתון בצוות או לשלב אותה בפרויקט. מאחר ו ctypes הוא מודול מובנה בפייתון, השילוב אפילו לא יוסיף לכם תלויות לפרויקט.
קוד? בטח. התוכנית הבאה טוענת את libc ומפעילה את הפונקציה rand מתוכה:
from ctypes import *
from ctypes.util import find_library
libc_location = find_library('c')
libc = cdll.LoadLibrary(libc_location)
print(libc.rand())
פשוט לא? בשביל להעביר פרמטרים לתוכנית C יש לנו 3 סוגי משתנים מובנים ב ctypes שאפשר להעביר כמו שהם:
האוביקט None יהפוך ל NULL ב C
אוביקט Bytes יהפוך ל const chat *
ב C
מספר int יהפוך ל int ב C.
לכן בשביל להפעיל את הפונקציה mkdir וליצור תיקייה חדשה מתוך קוד C אני יכול לכתוב:
from ctypes import *
from ctypes.util import find_library
libc_location = find_library('c')
libc = cdll.LoadLibrary(libc_location)
libc.mkdir(b"newdir")
בשביל העברת פרמטרים יותר מתוחכמים אנחנו צריכים להגדיר את טיפוסי הנתונים של הפונקציות ב C. זו אגב נקודת התורפה של ספריית ctypes, שמקבלת מענה רק בספריה אחרת בשם cffi, אבל זה כבר סיפור לפוסט אחר. בינתיים בכל מקרה ובתוך ctypes הדרך להפעיל פונקציות שצריכות פרמטרים יותר מתוחכמים היא להגדיר את טיפוסי הנתונים של אותן פונקציות. לדוגמה הפונקציה pow של libm מקבלת שני double-ים ומחזירה double ולכן בשביל להפעיל אותה אני כותב:
from ctypes import *
from ctypes.util import find_library
libm = cdll.LoadLibrary(find_library('m'))
# Works - use c_double, returns c_double
libm.pow.restype = c_double
libm.pow.argtypes = [c_double, c_double]
print(libm.pow(c_double(2), c_double(3)))
ואפשר כמובן להגדיר גם מבני נתונים מסובכים יותר כמו struct-ים ופונקציות, לדוגמה בפוסט כאן הראיתי איך להעביר פונקציית Callback מפייתון לפונקציית C בעזרת ספריית ctypes.
סך הכל ctypes מספקת פיתרון מאוד נוח כשאנחנו צריכים לגשת לספריה מקומפלת קיימת מתוך קוד פייתון. ככל שהספריה תהיה יותר גדולה והממשק יותר מתוחכם, אולי נרצה להוסיף פיתרון כמו cffi שיודע לקרוא הגדרות של פונקציות בשפת C ולתרגם אותן אוטומטית לפייתון, וכך לחסוך לעצמנו את ההגדרה הכפולה.