{ "metadata": { "name": "" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "[Python para Desenvolvedores](http://ricardoduarte.github.io/python-para-desenvolvedores/#conteudo)\n", "===================================\n", "2ª edi\u00e7\u00e3o, revisada e ampliada\n", "-----------------------------------\n", "\n", "Ap\u00eandice F: Integra\u00e7\u00e3o com outras linguagens\n", "=============================\n", "_____________________________\n", "Existe hoje muito c\u00f3digo legado desenvolvido em diversas linguagens que pode ser aproveitado pelo Python, atrav\u00e9s de v\u00e1rias formas de integra\u00e7\u00e3o.\n", "\n", "Uma forma gen\u00e9rica de fazer isso \u00e9 gerar uma biblioteca compartilhada (*shared library*) atrav\u00e9s do compilador da outra linguagem e fazer chamadas a fun\u00e7\u00f5es que est\u00e3o definidas na biblioteca.\n", "\n", "Como a implementa\u00e7\u00e3o original do Python \u00e9 usando Linguagem C, \u00e9 poss\u00edvel integrar Python e C nos dois sentidos:\n", "\n", "+ Python -> C (Python faz chamadas a um m\u00f3dulo compilado em C).\n", "+ C -> Python (C evoca o interpretador Python em modo *embedded*).\n", "\n", "Tamb\u00e9m \u00e9 poss\u00edvel integrar o Python com Fortran usando o utilit\u00e1rio f2py, que faz parte do projeto NumPy.\n", "\n", "Bibliotecas compartilhadas\n", "--------------------------\n", "A partir da vers\u00e3o 2.5, o Python incorporou o m\u00f3dulo *ctypes*, que implementa tipos compat\u00edveis com os tipos usados pela linguagem C e permite evocar fun\u00e7\u00f5es de bibliotecas compartilhadas.\n", "\n", "O m\u00f3dulo prov\u00ea v\u00e1rias formas de evocar fun\u00e7\u00f5es. Fun\u00e7\u00f5es que seguem a conven\u00e7\u00e3o de chamada *stdcall*, como a API do Windows, podem ser acessadas atrav\u00e9s da classe *windll*. *Dynamic-link library* (DLL) \u00e9 a implementa\u00e7\u00e3o de bibliotecas compartilhadas que s\u00e3o usadas no Windows.\n", "\n", "Exemplo com *windll*:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import ctypes\n", "\n", "# Evocando a caixa de mensagens do Windows\n", "# Os argumentos s\u00e3o: janela pai, mensagem,\n", "# t\u00edtulo da janela e o tipo da janela.\n", "# A fun\u00e7\u00e3o retorna um inteiro, que\n", "# corresponde a que bot\u00e3o foi pressionado\n", "i = ctypes.windll.user32.MessageBoxA(None,\n", " 'Teste de DLL!', 'Mensagem', 0)\n", "\n", "# O resultado indica qual bot\u00e3o foi clicado\n", "print i" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Para fun\u00e7\u00f5es que seguem a conven\u00e7\u00e3o de chamada *cdecl*, usada pela maioria dos compiladores C, existe a classe *cdll*. Para as passagens de argumentos por refer\u00eancia \u00e9 preciso criar uma vari\u00e1vel que funciona como um *buffer* para receber os resultados. Isso \u00e9 necess\u00e1rio para receber *strings*, por exemplo.\n", "\n", "Exemplo com *cdll* e *buffer*:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import ctypes\n", "\n", "# msvcrt \u00e9 a biblioteca com a maioria das fun\u00e7\u00f5es\n", "# padr\u00f5es da linguagens C no Windows\n", "# O Windows coloca automaticamente\n", "# a extens\u00e3o do arquivo\n", "clib = ctypes.cdll.msvcrt\n", "\n", "# Cria um buffer para receber o resultado\n", "# a refer\u00eancia para o buffer ser\u00e1 passada para\n", "# a fun\u00e7\u00e3o, que preenche o buffer com o resultado\n", "s = ctypes.c_buffer('\\000', 40)\n", "\n", "# sscanf() \u00e9 uma fun\u00e7\u00e3o que extrai valores\n", "# de uma string conforme uma mascara\n", "clib.sscanf('Testando sscanf!\\n',\n", " 'Testando %s!\\n', s)\n", "\n", "# Mostra o resultado\n", "print s.value" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\u00c9 poss\u00edvel tamb\u00e9m evocar fun\u00e7\u00f5es de bibliotecas compartilhadas no Linux:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import ctypes\n", "\n", "# Carrega a biblioteca padr\u00e3o C no Linux\n", "# A extens\u00e3o do arquivo precisa passada\n", "# para a fun\u00e7\u00e3o LoadLibrary()\n", "clib = ctypes.cdll.LoadLibrary(\"libc.so.6\")\n", "\n", "# Cria um buffer para receber o resultado\n", "s = ctypes.c_buffer('\\000', 40)\n", "\n", "# Evoca a fun\u00e7\u00e3o sprintf\n", "clib.sprintf(s, 'Testando %s\\n', 'sprintf!')\n", "\n", "# Mostra o resultado\n", "print s.value" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Atrav\u00e9s de bibliotecas compartilhadas \u00e9 poss\u00edvel usar c\u00f3digo desenvolvido em outras linguagens de uma maneira simples.\n", "\n", "Python -> C\n", "-----------\n", "O m\u00f3dulo escrito em C deve utilizar as estruturas do Python (que est\u00e3o definidas na API de interface) para se comunicar com o interpretador Python.\n", "\n", "Exemplo:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "// Arquivo: mymodule.c\n", "\n", "// Python.h define as estruturas do Python em C\n", "#include \n", "\n", "// No Python, mesmo os erros sao objetos\n", "static PyObject *MyModuleError;\n", "\n", "// Chamando a funcao \"system\" em C\n", "static PyObject *\n", "mymodule_system(PyObject *self, PyObject *args)\n", "{\n", " const char *command;\n", " int sts;\n", "\n", " // \"PyArg_ParseTuple\" desempacota a tupla de parametros\n", " // \"s\" significa que ele deve identificar uma string\n", " if (!PyArg_ParseTuple(args, \"s\", &command))\n", " // retornando NULL gera uma excessao\n", " // caso falte parametros\n", " return NULL;\n", "\n", " // chamando \"system\":\n", " sts = system(command);\n", "\n", " // \"Py_BuildValue\" gera objetos que o Python conhece\n", " // \"i\" significa inteiro\n", " return Py_BuildValue(\"i\", sts);\n", "}\n", "\n", "// Tabela que o Python consulta para resolver\n", "// os metodos do modulo e pode ser usado\n", "// tambem para gerar a documentacao\n", "// por instrospeccao: dir(), help(),...\n", "static PyMethodDef MyModuleMethods[] = {\n", " {\"system\", mymodule_system, METH_VARARGS,\n", " \"Executa comandos externos.\"},\n", " // Fim da tabela:\n", " {NULL, NULL, 0, NULL}\n", "};\n", "\n", "// inicializacao do modulo:\n", "PyMODINIT_FUNC\n", "initmymodule(void)\n", "{\n", " // O modulo tambem e' um objeto\n", " PyObject *m;\n", "\n", " // \"Py_InitModule\" precisa do nome do modulo e da\n", " // tabela de metodos\n", " m = Py_InitModule(\"mymodule\", MyModuleMethods);\n", "\n", " // Erros...\n", " MyModuleError = PyErr_NewException(\"mymodule.error\",\n", " NULL, NULL);\n", "\n", " // \"Py_INCREF\" incrementa o numero de referencias do objeto\n", " Py_INCREF(MyModuleError);\n", "\n", " // \"PyModule_AddObject\" adiciona um objeto ao modulo\n", " PyModule_AddObject(m, \"error\", MyModuleError);\n", "}" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ao inv\u00e9s de compilar o m\u00f3dulo manualmente, use o Python para automatizar o processo. Primeiro, crie o *script*:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Arquivo: setup.py\n", "\n", "from distutils.core import setup, Extension\n", "\n", "mymodule = Extension('mymodule', sources = ['mymodule.c'])\n", "setup(name = 'MyPackage', version = '1.0',\n", " description = 'My Package',\n", " ext_modules = [mymodule])" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "E para compilar:\n", "\n", " python setup.py build\n", " \n", "O bin\u00e1rio compilado ser\u00e1 gerado dentro da pasta \u201cbuild\u201d. O m\u00f3dulo pode ser usado como qualquer outro m\u00f3dulo no Python (atrav\u00e9s de *import*).\n", "\n", "C -> Python\n", "-----------\n", "O inverso tamb\u00e9m \u00e9 poss\u00edvel. Um programa escrito em C pode evocar o interpretador Python seguindo tr\u00eas passos:\n", "\n", "+ Inicializar o interpretador.\n", "+ Interagir (que pode ser feito de diversas formas).\n", "+ Finalizar o interpretador.\n", "\n", "Exemplo:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "// Arquivo: py_call.c\n", "\n", "// Python.h com as definicoes para\n", "// interagir com o interpretador\n", "#include \n", "\n", "int main()\n", "{\n", " // Inicializa interpretador Python\n", " Py_Initialize();\n", "\n", " // Executando codigo Python\n", " PyRun_SimpleString(\"import os\\n\"\n", " \"for f in os.listdir('.'):\\n\"\n", " \" if os.path.isfile(f):\\n\"\n", " \" print f, ':', os.path.getsize(f)\\n\");\n", "\n", " // Finaliza interpretador Python\n", " Py_Finalize();\n", " return 0;\n", "}" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Para compilar, \u00e9 preciso passar a localiza\u00e7\u00e3o das headers e libraries do Python para o compilador C:\n", "\n", " gcc -I/usr/include/python2.5 \\\n", " -L/usr/lib/python2.5/config \\\n", " -lpython2.5 -opy_call py_call.c\n", "\n", "Observa\u00e7\u00f5es:\n", "\n", "+ Esta API faz parte do CPython (porte do Python escrito em C).\n", "+ Existem ferramentas para automatizar o processo para gerar interfaces para sistemas maiores: SWIG, Boost.Python e SIP." ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [ { "html": [ "\n", "" ], "output_type": "pyout", "prompt_number": 1, "text": [ "" ] } ], "prompt_number": 1 } ], "metadata": {} } ] }