بايثون بأنواع C مترجمة
Cython مترجم للغة هي بايثون مضافًا إليها تصريحات أنواع C اختيارية. تكتب ملف .pyx ويترجمه Cython إلى C ثم يحوّله مترجم C إلى وحدة امتداد فتستوردها كأي وحدة أخرى. وشيفرة بايثون العادية المترجمة هكذا تعمل أسرع قليلًا أصلًا، لكن المكسب الحقيقي يأتي عند التصريح بالأنواع: cdef double total تخبر Cython أن total عدد آلة لا كائن بايثون فتعمل الحلقة على أرقام خام بلا كلفة المفسّر. ويمكن للحلقات العددية الثقيلة أن تصير أسرع بخمسين إلى مئة مرة كما مع Numba، لكنك تحصل على وحدة مترجمة عادية يمكنك توزيعها.
# What a Cython file looks like: Python with cdef type declarations (shown here as text)
pyx = """\
cpdef double dot(double[:] a, double[:] b):
cdef double total = 0.0
cdef long i
for i in range(a.shape[0]):
total += a[i] * b[i]
return total
"""
print(pyx.splitlines()[0]) # cpdef double dot(double[:] a, double[:] b):
print(sum("cdef" in line for line in pyx.splitlines())) # 2 two typed declarations inside the functionالتصريح بالأنواع: cdef وcpdef وعروض الذاكرة
يصرّح cdef بمتغير C (cdef double total وcdef long i) أو بدالة لا يستدعيها إلا شيفرة C. ويصرّح cpdef بدالة قابلة للاستدعاء من بايثون ومن C، وهو المناسب لنقاط دخول الوحدة. أما def فتبقى دالة بايثون عادية. وتُمرَّر المصفوفات عروض ذاكرة مُنمَّطة مثل double[:] لمصفوفة أحادية البعد من doubles: تقبل مصفوفات NumPy دون نسخ وتُفهرَس بسرعة C. فتوقيع مثل cpdef double mean(double[:] x, long n) يستبدل الأنواع الديناميكية في بايثون بأنواع دقيقة، والنوع المحذوف أو المصرَّح به object يبقى كائن بايثون بطيئًا.
# Building the declarations as text: one table from Python type names to C types
C_TYPES = {"int": "long", "float": "double", "bool": "bint", "array[float]": "double[:]"}
def c_type(py_type):
return C_TYPES.get(py_type, "object")
def cdef_signature(name, params, returns):
result = "void" if returns is None else c_type(returns)
arguments = ", ".join(f"{c_type(t)} {p}" for p, t in params)
return f"cpdef {result} {name}({arguments})"
print(cdef_signature("mean", [("x", "array[float]"), ("n", "int")], "float")) # cpdef double mean(double[:] x, long n)
print(cdef_signature("log_event", [("message", "dict")], None)) # cpdef void log_event(object message)بناء الوحدة ومعرفة ما يجب تحديد نوعه
يطلب setup.py مع cythonize(["pricing.pyx"]) من setuptools ترجمة الملف وبناءه؛ ويُنتج python setup.py build_ext --inplace مكتبة مشتركة بجوار شيفرتك ويحمّلها import pricing. وللتجارب السريعة يترجم pyximport عند الاستيراد. ولمعرفة ما يبقى بطيئًا شغّل cython -a pricing.pyx: يكتب صفحة HTML يُظلَّل فيها كل سطر بالأصفر بقدر ما يستعمله من آلية بايثون، والأسطر البيضاء C خالصة. حدّد أنواع المتغيرات في الأسطر الصفراء داخل حلقاتك الساخنة ثم عطّل الفحوص التي أثبتَّ عدم لزومها بـ @cython.boundscheck(False) و@cython.wraparound(False) ليصير الفهرسة رخيصة كما في C.
# The build script is a few lines of text; this generates it for a list of modules
def build_setup(modules):
files = ", ".join(f'"{module}.pyx"' for module in modules)
return "from setuptools import setup\nfrom Cython.Build import cythonize\n\n" + f"setup(ext_modules=cythonize([{files}]))\n"
print(build_setup(["pricing", "risk"]), end="")
# from setuptools import setup
# from Cython.Build import cythonize
#
# setup(ext_modules=cythonize(["pricing.pyx", "risk.pyx"]))# The loop a .pyx file would hold, as plain Python: same result, compiled once types are added
def dot(a, b):
total = 0.0
for i in range(len(a)):
total += a[i] * b[i]
return total
prices = [184.25, 410.5, 99.0]
quantities = [100.0, 50.0, 200.0]
print(dot(prices, quantities)) # 58750.0 the compiled version returns exactly this number
print(dot(prices, quantities) == sum(p * q for p, q in zip(prices, quantities))) # True- يترجم Cython ملف
.pyx(بايثون مع أنواع C اختيارية) إلى C ثم إلى وحدة امتداد تستوردها بـimportكالمعتاد. cdefيصرّح بمتغيرات C ودوال C فقط وcpdefبدالة قابلة للاستدعاء من بايثون وC؛ والمصفوفات عروض ذاكرة مُنمَّطة مثلdouble[:].- ابنِ بـ
cythonizeفيsetup.pyواستخدمcython -aلإيجاد الأسطر الصفراء (البطيئة) في الحلقات الساخنة وتحقق بنتيجة بايثون العادية.
التالي: استدعاء C++ باستخدام pybind11، إتاحة شيفرة C++ الموجودة لبايثون.
المختبر: ما الذي يُقيَّم
- اربط أنواع بايثون بأنواع C باستخدام
c_type25 نقطة - اكتب ترويسة دالة باستخدام
cdef_signature25 نقطة - حدّد أنواع المتغيرات المحلية باستخدام
declare_locals25 نقطة - اكتب سكربت البناء باستخدام
build_setup25 نقطة