Última actualización:2026-10-12
Notas sobre el desarrollo de extensiones de CPython¶
Este artículo girará en torno al trasfondo de una feature que envié recientemente a Apache para hablar de los problemas encontrados con CPython y del intento de escribir una biblioteca propia desde cero; el código también está en la comunidad 星球 y quien esté interesado puede descargarlo para estudiarlo.
0. Contexto¶
Últimamente, una feature que envié a apache arrow provocó, por un cambio en las interfaces de C++, cambios en las interfaces de otros lenguajes, y algunas pruebas hubo que repararlas en consecuencia.
Quien conozca PyArrow no le resultará nada nuevo: este cambio de interfaz también obligó a modificar esa biblioteca, ya que está en el mismo repositorio; de lo contrario, el ci no pasa. Y la implementación de PyArrow se hace mediante CPython; como yo no había estudiado CPython en especial, introduje un montón de problemas. Dos de ellos son bastante importantes y los registro aquí.
Problema 1: la función de inicialización no admite la construcción por defecto de otras clases.
Ejemplo:
Error:
TypeError: descriptor '_scalar' for 'pyarrow._compute.Expression' objects doesn't apply to a 'bool' object
Se ve que no lo reconoce, aunque en realidad Expression._scalar(True) es válido; miremos su implementación:
@staticmethod
def _scalar(value):
cdef:
Scalar scalar
if isinstance(value, Scalar):
scalar = value
else:
scalar = lib.scalar(value)
return Expression.wrap(CMakeScalarExpression(scalar.unwrap()))
Como se ve, dentro admite el tipo bool normal; sospeché que era una limitación de cpython, así que cambié a la forma siguiente y ya funcionó:
def __init__(self, mode="only_valid", filter=None):
if filter is None:
filter = Expression._scalar(True)
Problema 2: el orden de definición
Al usar el _true creado más abajo, cada vez que se pasa como valor por defecto llega vacío; esto se entiende bien, porque al final, una vez compilado, todo se traduce a un archivo xxx.cpp y, según las reglas de C++, lo que se lee antes está naturalmente vacío.
def __init__(self, mode="only_valid", filter=_true):
pass
cdef CExpression _true = CMakeScalarExpression(
<shared_ptr[CScalar]> make_shared[CBooleanScalar](True)
)
Bien, con este trasfondo, a mí también me apetecía escribir un ejemplo: usar C++ para escribir una clase que encapsule sort y sum, y luego llamarla desde Python.
1. Ejemplo completo de CPython¶
- Crear un archivo .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;
}
2) Crear foo.pyx
Punto importante: el vector anterior necesita:
Después define una class que llame a la interfaz de 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)
- Crear el archivo setup.py
ext = Extension('Foo', sources=["foo.pyx"], language="c++", include_dirs=[numpy.get_include()])
setup(name="Foo", ext_modules = cythonize([ext]))
- Ejecutar
Por último, puedes escribir un script de prueba para usar tu propia interfaz de python.
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))
CPython se usa bastante en algunos proyectos; ¡ponte a aprenderlo~
Ejecución:
Temas relacionados¶
- Seis métodos para dividir cadenas en C++ | C++那些事
- Chuleta de desarrollo con libcurl en C++ | C++那些事
- Cómo leer código C++ de código abierto | C++那些事
- Un fragmento de código C++ interesante | C++那些事
- Imprimir 1-100 en C++ sin bucles | C++那些事