Authors: | Felipe Cruz |
---|---|
Date: | 2012-11-06 |
Apresentação Pessoal
- Trabalho há 3/4 anos com Python.
- Já contribuí em Projetos OpenSource com código Python e C
- twitter: @felipejcruz
- github: https://github.com/felipecruz
Explicar motivação
C ainda é uma linguagem muito usada e indiscutivelmente rápida.
Python é uma linguagem é muito usada, flexível e produtiva.
Como unir os 2 mundos?
Suportadas Oficialmente
- Python C Extensions
- Ctypes
Terceiros
- Cython
- Cffi
- Pyrex
- (R)Python
- PyAutoC
- SWIG
- http://docs.python.org/3.3/extending/extending.html
- C usando a API C/Pyhton.
- Exige compilação.
- Debug mais complexo.
- Compatível com PyPy através do Cpyext (não recomendado).
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"pyaio", /* m_name */
"Python POSIX aio (aio.h) bindings",/* m_doc */
-1, /* m_size */
PyaioMethods, /* m_methods */
NULL, /* m_reload */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
};
static PyMethodDef PyaioMethods[] = {
{ "aio_read", pyaio_read,
METH_VARARGS, pyaio_read_doc },
{ "aio_write", pyaio_write,
METH_VARARGS, pyaio_write_doc },
{ NULL, NULL, 0, NULL }
};
PyObject *
init_pyaio(void)
{
PyObject *m;
PyObject *__version__;
__version__ = PyUnicode_FromFormat("%s",
PYAIO_VERSION);
if (!__version__) {
return NULL;
}
...
Isso é apenas uns 15% do necessário.
- Permite desenvolver módulos inteiros em C, não somente integrar libs em C.
- Tem muitos detalhes e certa complexidade.
- Bom desempenho.
- Pra testar é necessário compilar e instalar.
- Exige mais tempo e mais cuidado.
- Permite apenas chamar código em de bibliotecas escritas em C.
- Python puro.
- API relativamente simples.
- Dependendo do uso, performance pode ser um problema.
from ctypes import *
from ctypes.util import find_library
from ctypes import *
from ctypes.util import find_library
# achar uma biblioteca
zmq = CDLL(find_library("zmq"), use_errno=True)
Descrição dos métodos:
zmq.zmq_init.restype = c_void_p
zmq.zmq_init.argtypes = [c_int]
Descrição dos métodos:
zmq.zmq_init.restype = c_void_p
zmq.zmq_init.argtypes = [c_int]
# chamada do método definido
ctx = zmq.zmq_init(1)
zmq.zmq_socket.restype = c_int
zmq.zmq_socket.argtypes = [c_void_p, c_int]
zmq.zmq_socket.restype = c_int
zmq.zmq_socket.argtypes = [c_void_p, c_int]
# chamada de zmq_socket
socket = zmq.zmq_socket(ctx, c_int(1))
- Para entregar uma execução pesada para um código eficiente em C ou se foram poucas trocas do contexto Python para o contexto C, performance não é problema.
- Se exigir muitas "trocas de contexto", entre Python e C, performance pode ser um problema porque essa troca tem um custo.
- Prático para integrar com código que já existe.
- Fácil de causar SEGFAULT no interpretador.
from ctypes import c_char_p; c_char_p(1).value
- Compatível com PyPy (embora não seja a melhor opção).
- Python puro, testes não dependem de compilação/instalação.
- Linguagem que mistura C e Python ou Python "anotado".
- Performance equivalente a extensões nativas(C-Extensions).
- Depende de compilação.
- Debug baseado no GDB (em código C).
- Diversos níveis de uso e complexidade. (o que é bom!)
x = cython.declare(cython.int)
@cython.locals(a=cython.double,
b=cython.double)
def foo(a, b):
...
cython.declare(x=cython.int,
x_ptr=cython.p_int)
x_ptr = cython.address(x)
def soma(a, b):
return a + b
cdef int soma(int a, int b):
return a + b
$ cython arquivo.pyx
Depois, ou compila "na mão" ou usa setuptools/distribute para compilar e distribuir.
- Boa opção para escrever extensões nativas poupando trabalho braçal.
- É possível começar só com python puro.
- Compatibilidade com PyPy em andamento.
- Para escrever bindings*.
- Baseado na API LuaJIT FFI.
- Compatível com PyPy(trunk) e seu código é especializado pelo JIT.
- ABI & API level.
declarations =
'''
void *zmq_init(int io_threads);
void *zmq_socket(void* context, int type);
'''
from cffi import FFI
ffi = FFI()
declarations = '''
void *zmq_init(int io_threads);
void *zmq_socket(void* context, int type);
'''
headers = '#include <zmq.h>'
ffi.cdef(declarations)
zmq = ffi.verify(headers)
zmq_ctx = zmq.zmq_init(1)
zmq_socket = zmq.zmq_socket(zmq_ctx, ZMQ_PAIR)
c_string = ffi.new('char[]', 'uma string')
zmq_msg_t = ffi.new('zmq_msg_t*')
- Usar declarações em C, de um header file (.h) por exemplo.
- API simples
- Existe compilação mas é transparente. Testar é fácil.
- Ótimo desempenho com PyPy (trunk).
from pypy.translator.tool.cbuild import ExternalCompilationInfo
from pypy.rpython.lltypesystem import rffi
eci = ExternalCompilationInfo(includes=['zmq.h'],
libraries=['zmq'])
zmq_init = rffi.llexternal("zmq_init",
[rffi.INT],
rffi.VOIDP,
compilation_info=eci)
zmq_socket = rffi.llexternal("zmq_socket",
[rffi.VOIDP, rffi.UINT],
rffi.VOIDP,
compilation_info=eci)
def main(argv):
ctx = zmq_init(1)
socket = zmq_socket(ctx, 8)
zmq_connect(socket, "tcp://127.0.0.1:5555")
#msg = malloc(sizeof(zmsg_t))
msg = rffi.lltype.malloc(zmsg_t.TO, flavor='raw')
zmq_msg_init_size(msg, 5)
#memcpy already available!
rffi.c_memcpy(zmq_msg_data(msg), "aaaaa", 5)
def target(*args):
return main, None
Perguntas? Obrigado!