ctypes - ระดับเริ่มต้น


102

ฉันมีภารกิจในการ "ตัด" ไลบรารี ac ลงในคลาส python เอกสารมีความคลุมเครืออย่างไม่น่าเชื่อในเรื่องนี้ ดูเหมือนว่าพวกเขาคาดหวังว่าจะมีเพียงผู้ใช้ python ขั้นสูงเท่านั้นที่ใช้ ctypes ได้ ฉันเป็นมือใหม่ใน python และต้องการความช่วยเหลือ

ความช่วยเหลือทีละขั้นตอนจะดีมาก

ฉันจึงมีห้องสมุด c ของฉัน ฉันจะทำอย่างไร? ฉันวางไฟล์อะไรไว้ที่ไหน ฉันจะนำเข้าไลบรารีได้อย่างไร ฉันอ่านพบว่าอาจมีวิธี "ตัดอัตโนมัติ" เป็น Python?

(โดยวิธีการที่ฉันทำแบบฝึกหัด ctypes บน python.net และไม่ได้ผลหมายความว่าฉันคิดว่าพวกเขาคิดว่าฉันควรจะสามารถเติมเต็มขั้นตอนที่เหลือได้

อันที่จริงนี่เป็นข้อผิดพลาดที่ฉันได้รับจากรหัสของพวกเขา:

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

ฉันสามารถใช้ความช่วยเหลือทีละขั้นตอนในเรื่องนี้ได้จริงๆ! ขอบคุณ ~


10
คุณมี>>> ใน importtest.py หรือไม่? เมื่อมีคนโพสต์รหัสที่มี>>> ในแต่ละบรรทัดหมายความว่ากำลังรันในเชลล์แบบโต้ตอบ หากต้องการเรียกใช้จากไฟล์ให้ลบ>>> (นั่นคือ 3> เครื่องหมายและช่องว่าง) ทุกที่ที่ปรากฏ
Chinmay Kanchi

4
อย่าพิมพ์>>>s สิ่งเหล่านี้ถูกพิมพ์โดยเชลล์แบบโต้ตอบและควรถูกทิ้งไว้ในไฟล์ต้นฉบับของคุณ
nmichaels

8
>>>ในไฟล์. py! อุ๊ย! ไม่เคยเห็นมาก่อน!
David Heffernan

4
จริงๆแล้วเรียนรู้ Python (อย่างน้อยก็เล็กน้อย) ก่อนที่คุณจะเริ่มยุ่งกับ ctypes คุณจะไม่พบบทช่วยสอนเกี่ยวกับ ctypes ที่ถือว่าคุณไม่รู้จัก Python พื้นฐาน
Chinmay Kanchi

3
@spentak: หากคุณต้องการความช่วยเหลือโปรดให้ข้อมูลที่เพียงพอ อย่างน้อยก็แสดงโค้ดเวอร์ชันล่าสุดที่คุณกำลังพูดถึง ใน "บรรทัดที่ 3" คืออะไร?
Francesco

คำตอบ:


230

นี่คือบทแนะนำ ctypes ที่รวดเร็วและสกปรก

ขั้นแรกเขียนไลบรารี C ของคุณ นี่คือตัวอย่างง่ายๆของ Hello world:

testlib.c

#include <stdio.h>

void myprint(void);

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

ตอนนี้รวบรวมเป็นไลบรารีที่ใช้ร่วมกัน ( พบ mac fix ที่นี่ ):

$ 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

จากนั้นเขียนกระดาษห่อโดยใช้ ctypes:

testlibwrapper.py

import ctypes

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

ตอนนี้ดำเนินการ:

$ python testlibwrapper.py

และคุณจะเห็นผลลัพธ์

Hello world
$

หากคุณมีไลบรารีอยู่แล้วคุณสามารถข้ามส่วนที่ไม่ใช่ python ของบทช่วยสอนได้ ตรวจสอบให้แน่ใจว่า ctypes สามารถค้นหาไลบรารีได้โดยใส่ไว้ใน/usr/libไดเร็กทอรีมาตรฐานอื่น หากคุณทำเช่นนี้คุณไม่จำเป็นต้องระบุเส้นทางแบบเต็มเมื่อเขียนกระดาษห่อหุ้ม หากคุณเลือกที่จะไม่ทำเช่นนี้คุณจะต้องctypes.CDLL()จัดให้มีเส้นทางแบบเต็มของห้องสมุดเมื่อโทร

นี่ไม่ใช่สถานที่สำหรับบทช่วยสอนที่ครอบคลุมมากขึ้น แต่หากคุณขอความช่วยเหลือเกี่ยวกับปัญหาเฉพาะในไซต์นี้ฉันมั่นใจว่าชุมชนจะช่วยคุณได้

PS: ctypes.CDLL('libc.so.6')ฉันสมมติว่าคุณอยู่ในลินุกซ์เพราะคุณเคยใช้ หากคุณใช้ OS อื่นอาจมีการเปลี่ยนแปลงเล็กน้อย (หรือค่อนข้างมาก)


1
@ Chinmay: ฉันขอรหัสที่คล้ายกันสำหรับ Windows แทน C ได้ไหมโปรดใส่ตัวอย่างภาพ c ++ ได้ไหม ฉันสามารถโหลดไลบรารีของฉันได้ แต่ฉันไม่สามารถเข้าถึงฟังก์ชันของฉันจากไฟล์. dll มันมักจะบอกว่า "function 'xyz" not found " คุณช่วยแนะนำวิธีแก้ปัญหานี้ให้ฉันได้ไหม ไชโย
Neophile

ฉันไม่รู้มากเกี่ยวกับการพัฒนา Windows แต่ดูเหมือนว่า Windows จะทำอะไรบางอย่างที่ไม่เป็นระเบียบบางทีอาจใช้รูปแบบการโทรที่แตกต่างออกไป? บางทีคุณอาจค้นหาการส่งออกฟังก์ชัน C ++ ของคุณโดยใช้ "extern C"?
Chinmay Kanchi

ใช่ฉันทำอย่างนั้น แต่ยังไม่มีโชค
Neophile

6
ขอบคุณสำหรับการสอนที่ง่ายต่อการปฏิบัติตามซึ่งแสดงการทำงานพื้นฐานของ ctype
okysabeni

1
ความรวดเร็วและสกปรกเป็นบทเรียนที่ดีที่สุดเสมอ
lurscher

56

คำตอบของ Chinmay Kanchi นั้นยอดเยี่ยม แต่ฉันต้องการตัวอย่างของฟังก์ชันที่ส่งผ่านและส่งคืนตัวแปร / อาร์เรย์ไปยังรหัส C ++ ฉันจะรวมไว้ที่นี่เผื่อว่าจะเป็นประโยชน์กับคนอื่น ๆ

การส่งผ่านและส่งคืนจำนวนเต็ม

รหัส C ++ สำหรับฟังก์ชันที่รับจำนวนเต็มและเพิ่มหนึ่งในค่าที่ส่งคืน

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

บันทึกเป็นไฟล์test.cppให้สังเกต"C" ภายนอกที่ต้องการ (ซึ่งสามารถลบออกได้สำหรับรหัส C) สิ่งนี้รวบรวมโดยใช้ g ++ โดยมีอาร์กิวเมนต์คล้ายกับคำตอบของ Chinmay Kanchi

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

รหัส Python ใช้load_libraryจากการnumpy.ctypeslibสมมติว่าพา ธ ไปยังไลบรารีที่ใช้ร่วมกันในไดเร็กทอรีเดียวกันกับสคริปต์ 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)

สิ่งนี้พิมพ์ได้ 6 ตามที่คาดไว้

การส่งและการพิมพ์อาร์เรย์

คุณยังสามารถส่งผ่านอาร์เรย์ดังต่อไปนี้สำหรับรหัส C เพื่อพิมพ์องค์ประกอบของอาร์เรย์

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

ซึ่งรวบรวมเหมือนก่อนหน้านี้และนำเข้าในลักษณะเดียวกัน รหัส Python พิเศษที่จะใช้ฟังก์ชันนี้จะเป็น

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)

โดยที่เราระบุอาร์เรย์ซึ่งเป็นอาร์กิวเมนต์แรกprint_arrayเป็นตัวชี้ไปยังอาร์เรย์ Numpy ของการจัดตำแหน่ง c_contiguous 64 บิตลอยและอาร์กิวเมนต์ที่สองเป็นจำนวนเต็มซึ่งจะบอกรหัส C ถึงจำนวนขององค์ประกอบในอาร์เรย์ Numpy จากนั้นพิมพ์โดยรหัส C ดังนี้

1.4
2.6
3.0

5
นี่เป็นคำตอบเสริมที่ยอดเยี่ยม - ความอัปยศไม่มีคำตอบที่ถูก
ติ๊ก

ไม่แน่ใจว่าชัดเจนเกินไป แต่มีข้อผิดพลาดในโค้ด import numpy as npมันจะหายไป มิฉะนั้นจะไม่สามารถหาnp.float64และสิ่งอื่น ๆ
เบ็น

11

ประการแรก: >>>โค้ดที่คุณเห็นในตัวอย่าง python เป็นวิธีระบุว่าเป็นรหัส Python ใช้เพื่อแยกรหัส Python ออกจากเอาต์พุต แบบนี้:

>>> 4+5
9

ที่นี่เราจะเห็นว่าบรรทัดที่ขึ้นต้นด้วย>>>รหัส Python และ 9 คือสิ่งที่ได้ผลลัพธ์นี่คือลักษณะที่ปรากฏหากคุณเริ่มล่าม Python ซึ่งเป็นสาเหตุที่ทำให้เป็นเช่นนั้น

คุณไม่เคยใส่>>>ส่วนลงใน.pyไฟล์

ที่ดูแลข้อผิดพลาดทางไวยากรณ์ของคุณ

ประการที่สอง ctypes เป็นเพียงหนึ่งในหลาย ๆ วิธีในการตัดไลบรารี Python วิธีอื่น ๆ คือ SWIGซึ่งจะดูไลบรารี Python ของคุณและสร้างโมดูลส่วนขยาย Python C ที่แสดง C API อีกวิธีหนึ่งคือการใช้Cython

ล้วนมีประโยชน์และข้อเสีย

SWIG จะเปิดเผย C API ของคุณกับ Python เท่านั้น นั่นหมายความว่าคุณไม่ได้รับวัตถุหรืออะไรเลยคุณจะต้องสร้างไฟล์ Python แยกต่างหากเพื่อทำสิ่งนั้น อย่างไรก็ตามเป็นเรื่องปกติที่จะมีโมดูลที่เรียกว่า say "wowza" และโมดูล SWIG ที่เรียกว่า "_wowza" ซึ่งเป็น wrapper รอบ C API นี่เป็นวิธีที่ดีและง่ายในการทำสิ่งต่างๆ

Cython สร้างไฟล์ C-Extension มีประโยชน์ที่โค้ด Python ทั้งหมดที่คุณเขียนจะถูกสร้างเป็น C ดังนั้นอ็อบเจ็กต์ที่คุณเขียนจึงอยู่ใน C ด้วยซึ่งสามารถปรับปรุงประสิทธิภาพได้ แต่คุณจะต้องเรียนรู้ว่ามันเชื่อมต่อกับ C อย่างไรดังนั้นจึงเป็นเรื่องที่ต้องทำเพิ่มเติมเล็กน้อยเพื่อเรียนรู้วิธีใช้งาน

ctypes มีประโยชน์ที่ไม่มี C-code ให้คอมไพล์ดังนั้นจึงเป็นการดีมากที่จะใช้สำหรับการตัดไลบรารีมาตรฐานที่เขียนโดยบุคคลอื่นและมีอยู่แล้วในเวอร์ชันไบนารีสำหรับ Windows และ OS X

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.