Notas principais do Python para desenvolvimento de teste (17): depurador

17.1 Por que depurar

  • Há um problema com o próprio código, precisamos encontrar a causa raiz e corrigi-la
  • Existem problemas com a eficiência do código, como desperdício excessivo de recursos e alta latência, por isso precisamos depurar
  • Ao desenvolver novos recursos, o teste geralmente é necessário
  • Depuração para entender a lógica de processamento ao ler projetos de código aberto

17.2 Métodos de depuração

17.2.1 O método de depuração de impressão

Para programas simples, use a saída da função de impressão para depurar. Mas para programas grandes, pode haver muitos erros suspeitos e pode ser necessário escrever um grande número de funções de impressão no código. Após a depuração, exclua-o novamente. Obviamente mais problemático.

17.2.2 Pontos de interrupção do Pycharm

Pycharm é um ambiente de desenvolvimento integrado Python fácil de usar que fornece depuração de ponto de interrupção. Adicione um ponto de interrupção onde há suspeita de erro e, em seguida, execute o programa através do método Debug, o programa irá parar no ponto de interrupção, o que melhora muito a eficiência em comparação com o uso da função de impressão.

A maneira de adicionar um ponto de interrupção é clicar no lado direito do número da linha do Pycharm e clicar no botão esquerdo do mouse, para que o programa pare aqui quando for executado pelo Debug. Você também pode definir condições para pontos de interrupção, ou seja, parar quando determinadas condições forem atendidas. O método é clicar com o botão direito do mouse no ponto vermelho do ponto de interrupção, adicionar uma condição na coluna Condição e clicar em Concluído e descobrir que há uma ponto de interrogação no canto inferior direito do ponto. , indicando que este é um ponto de interrupção condicional.
insira a descrição da imagem aqui
Clique no botão Debug no Pycharm para iniciar a depuração do programa e a janela de depuração do Debugger será exibida em Pycharm. Esta janela é composta por três partes principais: a mais à esquerda são os Frames de informação da pilha de chamadas, a do meio são as informações de Variáveis ​​e a mais à direita são as informações de Watches.Esta janela pode adicionar as Variáveis ​​que nos interessam através do sinal +.
insira a descrição da imagem aqui
Quando o programa é parado, a execução do programa pode ser controlada através de uma fileira de botões acima da janela Variáveis.
insira a descrição da imagem aqui
O mais à esquerda significa deixar o programa continuar a executar uma linha, o segundo da esquerda é inserir a função na linha atual, o segundo da direita é pular para fora da função para a posição onde ela é chamada e o primeiro da direita é para executar na linha onde o cursor está localizado. Quando a execução do programa é controlada por esses botões, as informações da janela Frames, da janela Varibles e da janela Watches serão alteradas. Usamos essas informações para entender o processo intermediário de execução de código.

17.2.3 método de depuração pdb

pdb é um pacote para depuração interativa que vem com programas Python. Você precisa adicionar duas linhas de código "import pdb" e "pdb.set_trace()" ao programa. Existem vários comandos comuns no pdb: p, n, l, s, que podem ser aprendidos em combinação com o seguinte código:

import pdb

a = 1
b = 2
pdb.set_trace()  # 暂停了下来,等待用户输入


def echo(x):
    print(x)


c = 3
echo(a + b + c)

Para executar o código acima, eu uso os quatro comandos acima respectivamente, veja os comentários.

/usr/local/bin/python3.7 /Users/chunming.liu/PycharmProjects/mypytest/test/test_suite1/func.py
> /Users/chunming.liu/PycharmProjects/mypytest/test/test_suite1/func.py(8)<module>()
-> def echo(x):    # 运行到这一行停止
(Pdb) p a  # 打印变量a
1
(Pdb) n  # 执行下一行
> /Users/chunming.liu/PycharmProjects/mypytest/test/test_suite1/func.py(12)<module>()
-> c = 3
(Pdb) l  # 列出当前代码行前后11行代码
  7  	
  8  	def echo(x):
  9  	    print(x)
 10  	
 11  	
 12  ->	c = 3
 13  	echo(a + b + c)
[EOF]
(Pdb) n  # 执行下一行
> /Users/chunming.liu/PycharmProjects/mypytest/test/test_suite1/func.py(13)<module>()
-> echo(a + b + c)
(Pdb) s  # 进入到echo函数中
--Call--
> /Users/chunming.liu/PycharmProjects/mypytest/test/test_suite1/func.py(8)echo()
-> def echo(x):
(Pdb) p x
6
(Pdb) n
> /Users/chunming.liu/PycharmProjects/mypytest/test/test_suite1/func.py(9)echo()
-> print(x)
(Pdb) n
6
--Return--
> /Users/chunming.liu/PycharmProjects/mypytest/test/test_suite1/func.py(9)echo()->None
-> print(x)
(Pdb) 

Use o comando s para entrar no interior da função echo(), exiba –Call-; e quando terminarmos de executar a instrução interna da função echo() e pule para fora, exiba –Return-. Como alternativa, você pode b 11definir um ponto de interrupção na linha 11 com cExecutar até o próximo ponto de interrupção.

17.3 Métodos de Análise de Desempenho

cProfile fornece uma análise detalhada da eficiência de execução de cada bloco de código.

O cProfile calcula o tempo gasto em cada módulo, para saber onde está o gargalo do programa e corrigi-lo ou otimizá-lo.

Aqui está uma função que gera uma sequência de Fibonacci, analisada para desempenho com cProfile:

import cProfile


def fib(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fib(n - 1) + fib(n - 2)


def fib_seq(n):
    res = []
    if n > 0:
        res.extend(fib_seq(n - 1))
    res.append(fib(n))
    return res


cProfile.run('fib_seq(30)')  # 分析哪个函数,就将那个函数作为cProfile.run的参数

Produzirá, dados de perfil:

7049218 function calls (96 primitive calls) in 2.519 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    2.519    2.519 <string>:1(<module>)
     31/1    0.000    0.000    2.519    2.519 func.py:13(fib_seq)
7049123/31    2.519    0.000    2.519    0.081 func.py:4(fib)
        1    0.000    0.000    2.519    2.519 {
    
    built-in method builtins.exec}
       31    0.000    0.000    0.000    0.000 {
    
    method 'append' of 'list' objects}
        1    0.000    0.000    0.000    0.000 {
    
    method 'disable' of '_lsprof.Profiler' objects}
       30    0.000    0.000    0.000    0.000 {
    
    method 'extend' of 'list' objects}

Uma breve introdução aos dados de análise de desempenho:

  • ncalls, refere-se ao número de vezes que o código/função correspondente é chamado;
  • tottime, refere-se ao tempo total de execução do código/função correspondente (observe que não inclui o tempo de execução de outros códigos/funções que ele chama);
  • tottime percall é o resultado da divisão dos dois acima, ou seja, tottime/ncalls;
  • cumtime refere-se ao tempo total de execução do código/função correspondente, incluindo o tempo de execução de outros códigos/funções que ele chama;
  • cumtime percall é o resultado médio da divisão de cumtime e ncalls.

O gargalo da eficiência de execução deste programa é a função fib() na segunda linha, que é chamada mais de 7 milhões de vezes.

Há muitas chamadas para fib() no programa, mas na verdade há muitas repetições, então podemos usar um dicionário para salvar os resultados calculados para evitar cálculos repetidos. Use o decorador para otimizar o código acima:

def memoize(f):
    memo = {
    
    }

    def helper(x):
        if x not in memo:
            memo[x] = f(x)
        return memo[x]

    return helper


@memoize
def fib(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fib(n - 1) + fib(n - 2)


def fib_seq(n):
    res = []
    if n > 0:
        res.extend(fib_seq(n - 1))
    res.append(fib(n))
    return res


fib_seq(30)

A saída de desempenho desta vez é a seguinte: Pode-se ver que o número de chamadas da função fib é de apenas 31 vezes, o que melhora muito a eficiência de execução do código:

215 function calls (127 primitive calls) in 0.000 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 <string>:1(<module>)
       31    0.000    0.000    0.000    0.000 func.py:15(fib)
     31/1    0.000    0.000    0.000    0.000 func.py:25(fib_seq)
    89/31    0.000    0.000    0.000    0.000 func.py:7(helper)
        1    0.000    0.000    0.000    0.000 {built-in method builtins.exec}
       31    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
       30    0.000    0.000    0.000    0.000 {method 'extend' of 'list' objects}

Acho que você gosta

Origin blog.csdn.net/liuchunming033/article/details/107896897
Recomendado
Clasificación