Última atualização:2026-10-12
Notas sobre desenvolvimento de extensões CPython¶
Este artigo toma como pano de fundo um feature que enviei recentemente ao Apache para falar dos problemas encontrados com CPython, além da tentativa de escrever uma biblioteca do zero; o código também já foi colocado na planeta — quem se interessar pode baixar para estudar.
0.Contexto¶
Recentemente, um feature que submeti ao apache arrow provocou mudanças nas interfaces de outras linguagens por causa de uma mudança na interface C++, e alguns testes também precisaram ser corrigidos em seguida.
Quem está familiarizado com o PyArrow não vai estranhar nada: essa mudança de interface também exigiu modificar essa biblioteca, pois ela está no mesmo repositório — caso contrário, o ci não passa. E como o PyArrow é implementado via CPython, e eu nunca tinha estudado CPython a fundo, acabou surgindo uma série de problemas; dois deles são bem importantes e registro aqui.
Problema 1: a função de inicialização não dá suporte à construção padrão de outras classes.
Exemplo:
Erro: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)
Quando uso o _true criado mais abaixo, o valor padrão passado a cada vez é vazio — isso é fácil de entender, pois, no final, depois de compilado, tudo é traduzido para um arquivo xxx.cpp e, pelas regras do C++, o que é lido antes naturalmente fica vazio.
def __init__(self, mode="only_valid", filter=_true):
pass
cdef CExpression _true = CMakeScalarExpression(
<shared_ptr[CScalar]> make_shared[CBooleanScalar](True)
)
1.Exemplo completo de CPython¶
- Crie um arquivo .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;
}
Ponto importante: o vector acima precisa de:
Em seguida, defina uma class que chame a interface 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))
Execução:
Tópicos relacionados¶
- Seis métodos de divisão de strings em C++ | C++那些事
- Notas de desenvolvimento com libcurl em C++ | C++那些事
- Como ler código C++ open source | C++那些事
- Um trecho de código C++ interessante | C++那些事
- Imprimir de 1 a 100 em C++ sem laços | C++那些事