Uma tradução do artigo foi preparada especificamente para os alunos do curso Python Developer .
Nota : o código neste artigo está licenciado sob GNU AGPLv3 .Escrevi este guia porque não consegui encontrar um que combine todas as coisas úteis sobre os tipos. Espero que este artigo facilite muito a vida de alguém.Conteúdo:- Otimizações básicas
- estilos
- Compilação Python
- Estruturas em Python
- Ligue para seu código em C
- Pypy
Otimizações básicas
Antes de reescrever o código fonte do Python em C, considere os métodos básicos de otimização no Python.Estruturas de dados incorporadas
As estruturas de dados internas do Python, como set e dict, são escritas em C. Eles funcionam muito mais rápido que as suas próprias estruturas de dados escritas como classes Python. Outras estruturas de dados além do conjunto padrão, ditado, lista e tupla são descritas na documentação do módulo de coleções .Listar expressões
Em vez de adicionar itens à lista usando o método padrão, use expressões de lista.
mapped = []
for value in originallist:
mapped.append(myfunc(value))
mapped = [myfunc(value) in originallist]
ctypes
O módulo ctypes permite que você interaja com o código C do Python sem usar um módulo subprocess
ou outro módulo semelhante para iniciar outros processos a partir da CLI.Existem apenas duas partes: compilar o código C para carregar qualidade shared object
e configurar estruturas de dados no código Python para mapeá-las para os tipos C.Neste artigo, conectarei meu código Python ao lcs.c , que encontra a subsequência mais longa em dois listas de strings. Quero que o seguinte funcione em Python:list1 = ['My', 'name', 'is', 'Sam', 'Stevens', '!']
list2 = ['My', 'name', 'is', 'Alex', 'Stevens', '.']
common = lcs(list1, list2)
print(common)
Um problema é que essa função C específica é a assinatura de uma função que recebe listas de cadeias como tipos de argumento e retorna um tipo que não possui um comprimento fixo. Eu resolvo esse problema com uma estrutura de sequência contendo ponteiros e comprimento.Compilando código C em Python
Primeiro, o código-fonte C ( lcs.c ) é compilado lcs.so
para carregar no Python.gcc -c -Wall -Werror -fpic -O3 lcs.c -o lcs.o
gcc -shared -o lcs.so lcs.o
- - Wall exibirá todos os avisos;
- - Werror irá quebrar todos os avisos em erros;
- - o fpic irá gerar instruções independentes da posição que você precisará se quiser usar esta biblioteca em Python;
- - O3 maximiza a otimização;
E agora começaremos a escrever código Python usando o arquivo de objeto compartilhado resultante .Estruturas em Python
Abaixo estão duas estruturas de dados que são usadas no meu código C.struct Sequence
{
char **items;
int length;
};
struct Cell
{
int index;
int length;
struct Cell *prev;
};
E aqui está a tradução dessas estruturas em Python.import ctypes
class SEQUENCE(ctypes.Structure):
_fields_ = [('items', ctypes.POINTER(ctypes.c_char_p)),
('length', ctypes.c_int)]
class CELL(ctypes.Structure):
pass
CELL._fields_ = [('index', ctypes.c_int), ('length', ctypes.c_int),
('prev', ctypes.POINTER(CELL))]
Algumas notas:- Todas as estruturas são classes que herdam
ctypes.Structure
. - O único campo
_fields_
é uma lista de tuplas. Cada tupla é ( <variable-name>
, <ctypes.TYPE>
). - Existem
ctypes
tipos semelhantes em c_char (char) e c_char_p (* char) . - Também existe
ctypes
um POINTER()
que cria um ponteiro de tipo de cada tipo passado para ele. - Se você tem uma definição recursiva como em
CELL
, deve passar a declaração inicial e adicionar os campos _fields_
para obter um link para si mesmo posteriormente. - Como não usei
CELL
Python no meu código, não precisei escrever essa estrutura, mas ela possui uma propriedade interessante no campo recursivo.
Ligue para seu código em C
Além disso, eu precisava de algum código para converter tipos Python em novas estruturas em C. Agora você pode usar sua nova função C para acelerar o código Python.def list_to_SEQUENCE(strlist: List[str]) -> SEQUENCE:
bytelist = [bytes(s, 'utf-8') for s in strlist]
arr = (ctypes.c_char_p * len(bytelist))()
arr[:] = bytelist
return SEQUENCE(arr, len(bytelist))
def lcs(s1: List[str], s2: List[str]) -> List[str]:
seq1 = list_to_SEQUENCE(s1)
seq2 = list_to_SEQUENCE(s2)
common = lcsmodule.lcs(ctypes.byref(seq1), ctypes.byref(seq2))[0]
ret = []
for i in range(common.length):
ret.append(common.items[i].decode('utf-8'))
lcsmodule.freeSequence(common)
return ret
lcsmodule = ctypes.cdll.LoadLibrary('lcsmodule/lcs.so')
lcsmodule.lcs.restype = ctypes.POINTER(SEQUENCE)
list1 = ['My', 'name', 'is', 'Sam', 'Stevens', '!']
list2 = ['My', 'name', 'is', 'Alex', 'Stevens', '.']
common = lcs(list1, list2)
print(common)
Algumas notas:**char
(lista de strings) corresponde diretamente a uma lista de bytes em Python.- Existe
lcs.c
uma função lcs()
com a assinatura struct Sequence * lcs (struct Sequence * s1, struct Sequence * s2) . Para configurar o tipo de retorno, eu uso lcsmodule.lcs.restype = ctypes.POINTER(SEQUENCE)
. - Para fazer uma chamada com uma referência à estrutura Sequence , eu uso
ctypes.byref()
uma que retorna um "ponteiro leve" para o seu objeto (mais rápido que ctypes.POINTER()
). common.items
- esta é uma lista de bytes, eles podem ser decodificados para entrar ret
na forma de uma lista str
.- lcsmodule.freeSequence (common) libera apenas a memória associada ao common. Isso é importante porque o coletor de lixo (AFAIK) não o coleta automaticamente.
Código Python otimizado é o código que você escreveu em C e envolveu em Python.Algo mais: PyPy
Atenção: eu mesmo nunca usei o PyPy.Uma das otimizações mais simples é executar seus programas no tempo de execução do PyPy , que contém um compilador JIT (just-in-time) que acelera o trabalho de loops, compilando-os em código de máquina para execução repetida.Se você tiver comentários ou quiser discutir algo, escreva para mim (samuel.robert.stevens@gmail.com).Isso é tudo. Vejo você no curso !