Последнее обновление:2026-10-12
Заметки о разработке расширений CPython¶
Эта статья на фоне недавно отправленного в Apache feature расскажет о проблемах, возникших с CPython, и о попытке написать собственную библиотеку с нуля; код уже выложен в закрытом сообществе — все желающие могут скачать и изучить его.
0. Предыстория¶
Недавно отправленный мной в apache arrow feature из-за изменения C++-интерфейса повлёк изменение интерфейсов других языков, и некоторые тесты тоже пришлось чинить.
Те, кто знаком с PyArrow, прекрасно это поймут: на этот раз при изменении интерфейса пришлось править и эту библиотеку, так как она находится в том же репозитории — иначе CI не проходил. А реализация PyArrow сделана через CPython; раньше я толком не изучал CPython и в процессе правок натворил кучу проблем. Две из них оказались особенно важными — запишу их здесь.
Проблема 1: внутри инициализирующей функции не поддерживается вызов конструктора по умолчанию других классов.
Пример:
Ошибка:TypeError: descriptor '_scalar' for 'pyarrow._compute.Expression' objects doesn't apply to a 'bool' object
@staticmethod
def _scalar(value):
cdef:
Scalar scalar
if isinstance(value, Scalar):
scalar = value
else:
scalar = lib.scalar(value)
return Expression.wrap(CMakeScalarExpression(scalar.unwrap()))
def __init__(self, mode="only_valid", filter=None):
if filter is None:
filter = Expression._scalar(True)
Когда я использовал созданный позже _true, каждый раз передаваемое значение по умолчанию оказывалось пустым. Это довольно понятно: в итоге всё компилируется в один файл xxx.cpp, и по правилам C++ при чтении раньше определения там, естественно, пусто.
def __init__(self, mode="only_valid", filter=_true):
pass
cdef CExpression _true = CMakeScalarExpression(
<shared_ptr[CScalar]> make_shared[CBooleanScalar](True)
)
1. Полный пример на CPython¶
- Создаём файл .h
void sort(std::vector<int>& nums) {
std::sort(nums.begin(), nums.end());
}
int sum(std::vector<int>& nums) {
int sum = 0;
for (int num : nums) {
sum += num;
}
return sum;
}
Важный момент: для приведённого выше vector нужно:
Затем определяем class и вызываем C++-интерфейс.cdef class PyFoo:
cdef Foo* f
def __cinit__(self):
self.f = new Foo()
def __dealloc__(self):
del self.f
def sort(self, nums):
cdef vector[int] c_nums = nums
self.f.sort(c_nums)
def sum(self, nums):
cdef vector[int] c_nums = nums
return self.f.sum(c_nums)
ext = Extension('Foo', sources=["foo.pyx"], language="c++", include_dirs=[numpy.get_include()])
setup(name="Foo", ext_modules = cythonize([ext]))
import Foo
f = Foo.PyFoo()
nums = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
f.sort(nums)
print("Sorted nums:", nums)
print("Sum of nums:", f.sum(nums))
Запуск:
Похожие темы¶
- Шесть способов разделения строк в C++ | C++那些事
- Памятка по разработке на C++ с libcurl | C++那些事
- Как читать открытый C++-код | C++那些事
- Интересный фрагмент C++-кода | C++那些事
- Печать чисел от 1 до 100 без циклов в C++ | C++那些事