Domine o `timeit` Python: Cronometre seu código com precisão!

Neste guia, você descobrirá como empregar a função timeit do módulo homônimo em Python. Aprenderá a medir a duração de execução de expressões e funções simples no Python.

A cronometragem do seu código pode ser útil para obter uma estimativa do tempo necessário para a execução de um segmento de código, além de identificar as partes do código que podem necessitar de otimização.

Começaremos explorando a sintaxe da função timeit do Python. Em seguida, desenvolveremos exemplos para entender como utilizá-la para medir o tempo de execução de trechos de código e funções em seu módulo Python. Vamos iniciar.

Como Utilizar a Função timeit do Python

O módulo timeit faz parte da biblioteca padrão do Python, e você pode importá-lo da seguinte forma:

import timeit

A estrutura para empregar a função timeit do módulo é:

timeit.timeit(stmt, setup, number)

Onde:

  • stmt é o trecho de código cujo tempo de execução deve ser avaliado. Pode ser definido como uma string Python simples, uma string multilinhas, ou você pode passar o nome de um objeto chamável (callable).
  • setup, como o nome sugere, representa a porção de código que necessita ser executada uma única vez, geralmente como uma condição prévia para a execução do stmt. Por exemplo, imagine que você esteja medindo o tempo de execução para a criação de um array NumPy. Nesse caso, a importação do NumPy seria o código de configuração, e a criação do array seria a instrução a ser cronometrada.
  • number é o número de vezes que o stmt será executado. O valor padrão é 1 milhão (1.000.000), mas você tem a opção de ajustar este parâmetro para qualquer outro valor desejado.

Agora que compreendemos a sintaxe para usar a função timeit(), vamos começar a elaborar alguns exemplos práticos.

Medindo o Tempo de Expressões Python Simples

Nesta seção, tentaremos quantificar o tempo de execução de expressões Python básicas utilizando o timeit.

Inicie um Python REPL e execute os exemplos de código a seguir. Aqui, estamos medindo o tempo de execução das operações de exponenciação e divisão inteira para 10.000 e 100.000 repetições.

Note que a instrução a ser cronometrada é passada como uma string Python, e empregamos um ponto e vírgula para separar as diferentes expressões na instrução.

>>> import timeit
>>> timeit.timeit('3**4;3//4',number=10000)
0.0004020999999738706

>>> timeit.timeit('3**4;3//4',number=100000)
0.0013780000000451764

Executando timeit do Python na Linha de Comando

O timeit também pode ser utilizado diretamente da linha de comando. Aqui está o equivalente em linha de comando da chamada da função timeit:

$ python-m timeit -n [number] -s [setup] [stmt]
  • python -m timeit indica que estamos executando o timeit como o módulo principal.
  • n é uma opção de linha de comando que especifica o número de vezes que o código será executado, equivalente ao argumento number na chamada da função timeit().
  • A opção -s permite definir o código de configuração.

A seguir, reescrevemos o exemplo anterior utilizando o equivalente em linha de comando:

$ python -m timeit -n 100000 '3**4;3//4'
100000 loops, best of 5: 35.8 nsec per loop

Neste exemplo, medimos o tempo de execução da função interna len(). A inicialização da string é o código de configuração, passado através da opção s.

$ python -m timeit -n 100000 -s "string_1 = 'coding'" 'len(string_1)'
100000 loops, best of 5: 239 nsec per loop

Na saída, observe que o tempo de execução apresentado é o melhor de 5 execuções. O que isto quer dizer? Ao executar o timeit na linha de comando, a opção de repetição r assume o valor padrão de 5. Isto implica que a execução do stmt pelo número especificado de vezes é repetida cinco vezes, e o melhor tempo de execução é o que é exibido.

Análise de Métodos de Inversão de Strings Usando timeit

Ao lidar com strings em Python, a necessidade de invertê-las pode surgir. As duas abordagens mais comuns para a inversão de strings são:

  • Utilização de fatiamento de strings.
  • Utilização da função reversed() e do método join().

Invertendo Strings Python com Fatiamento de Strings

Vamos explorar como o fatiamento de strings funciona e como você pode utilizá-lo para inverter uma string Python. Ao empregar a sintaxe alguma-string[início:fim], uma fatia da string é retornada, começando no índice inicial e se estendendo até o índice final-1. Considere o exemplo a seguir:

Considere a string ‘Python’. A string tem um comprimento de 6, e a lista de índices é de 0, 1, 2 até 5.

>>> string_1 = 'Python'

Ao especificar os valores inicial e final, você obtém uma fatia da string que se estende do início até o fim-1. Portanto, string_1[1:4] retorna ‘yth’.

>>> string_1 = 'Python'
>>> string_1[1:4]
'yth'

Quando o valor inicial não é especificado, o valor inicial padrão de zero é empregado, e a fatia começa no índice zero, estendendo-se até o índice de parada – 1.

Aqui, o valor de parada é 3, então a fatia começa no índice 0 e vai até o índice 2.

>>> string_1[:3]
'Pyt'

Quando você omite o índice de parada, a fatia começa no índice inicial (1) e se estende até o final da string.

>>> string_1[1:]
'ython'

Omitir os valores inicial e final retorna uma fatia da string inteira.

>>> string_1[::]
'Python'

Vamos criar uma fatia com o valor do passo. Defina os valores de início, parada e etapa para 1, 5 e 2, respectivamente. Obtemos uma fatia da string que começa em 1 e se estende até 4 (excluindo o ponto final 5) contendo cada segundo caractere.

>>> string_1[1:5:2]
'yh'

Ao usar uma etapa negativa, você pode obter uma fatia começando no final da string. Com a etapa definida como -2, string_1[5:2:-2] produz a seguinte fatia:

>>> string_1[5:2:-2]
'nh'

Portanto, para obter uma cópia invertida da string, ignoramos os valores de início e parada e definimos a etapa como -1, conforme exibido:

>>> string_1[::-1]
'nohtyP'

Resumindo: cadeia[::-1] retorna uma cópia invertida da string.

Invertendo Strings Utilizando Funções Integradas e Métodos de String

A função integrada reversed() em Python retorna um iterador reverso sobre os elementos da string.

>>> string_1 = 'Python'
>>> reversed(string_1)
<reversed object at 0x00BEAF70>

Assim, você pode iterar sobre o iterador reverso utilizando um loop for:

for char in reversed(string_1):
    print(char)

E acessar os elementos da string na ordem inversa.

# Output
n
o
h
t
y
P

Em seguida, você pode chamar o método join() no iterador reverso com a seguinte sintaxe: .join(reversed(alguma-string)).

O trecho de código abaixo demonstra alguns exemplos onde o separador é um hífen e um espaço em branco, respectivamente.

>>> '-'.join(reversed(string1))
'n-o-h-t-y-P'
>>> ' '.join(reversed(string1))
'n o h t y P'

Aqui, não desejamos nenhum separador; portanto, definimos o separador como uma string vazia para obter uma cópia invertida da string:

>>> ''.join(reversed(string1))
'nohtyP'

O uso de .join(reversed(alguma-string)) retorna uma cópia invertida da string.

Comparando Tempos de Execução Usando timeit

Até agora, aprendemos duas abordagens para inverter strings em Python. Mas qual delas é a mais rápida? Vamos descobrir.

Em um exemplo anterior, onde cronometramos expressões simples em Python, não tínhamos nenhum código de configuração. Aqui, estamos invertendo a string Python. Enquanto a operação de reversão da string é executada o número de vezes especificado por number, o código de configuração é a inicialização da string que será executada somente uma vez.

>>> import timeit
>>> timeit.timeit(stmt="string_1[::-1]", setup = "string_1 = 'Python'", number = 100000)
0.04951830000001678
>>> timeit.timeit(stmt = "''.join(reversed(string_1))", setup = "string_1 = 'Python'", number = 100000)
0.12858760000000302

Para o mesmo número de execuções para inverter a string fornecida, a abordagem de fatiamento de string é mais rápida do que o método join() combinado com a função reversed().

Medindo Funções Python Usando timeit

Nesta seção, vamos explorar como medir o tempo de execução de funções Python com a função timeit. Dada uma lista de strings, a seguinte função hasDigit retorna a lista de strings que contêm ao menos um dígito.

def hasDigit(somelist):
     str_with_digit = []
     for string in somelist:
         check_char = [char.isdigit() for char in string]
         if any(check_char):
            str_with_digit.append(string)
     return str_with_digit

Agora, gostaríamos de avaliar o tempo de execução desta função Python hasDigit() utilizando timeit.

Inicialmente, vamos identificar a instrução a ser cronometrada (stmt). É a chamada à função hasDigit() com uma lista de strings como argumento. Em seguida, definiremos o código de configuração. Você consegue adivinhar qual deve ser o código de configuração?

Para que a chamada da função seja executada com sucesso, o código de configuração precisa incluir:

  • A definição da função hasDigit().
  • A inicialização da lista de strings que serão passadas como argumento.

Vamos definir o código de configuração na string de configuração, como exibido abaixo:

setup = """
def hasDigit(somelist):
    str_with_digit = []
    for string in somelist:
      check_char = [char.isdigit() for char in string]
      if any(check_char):
        str_with_digit.append(string)
    return str_with_digit
thislist=['puffin3','7frost','blue']
     """

Em seguida, podemos utilizar a função timeit e obter o tempo de execução da função hasDigit() para 100.000 execuções.

import timeit
timeit.timeit('hasDigit(thislist)',setup=setup,number=100000)
# Output
0.2810094920000097

Conclusão

Você aprendeu a empregar a função timeit do Python para medir o tempo de execução de expressões, funções e outros objetos chamáveis. Isto pode auxiliá-lo a avaliar seu código, comparar os tempos de execução de diferentes implementações da mesma funcionalidade e muito mais.

Vamos revisar o que aprendemos neste tutorial. Você pode empregar a função timeit() com a sintaxe timeit.timeit(stmt=…,setup=…,number=…). Alternativamente, você pode executar o timeit na linha de comando para cronometrar pequenos trechos de código.

Como próximo passo, você pode explorar como empregar outros pacotes de perfilagem do Python, como o line-profiler e o memprofiler, para gerar perfis do seu código em relação ao tempo e ao uso de memória, respectivamente.

Em seguida, aprenda a calcular a diferença de tempo em Python.