Coletânea Python do ZERO às Redes Neurais Artificiais

Fernando Feltrin · Capítulo 152 de 287

Páginas do PDF

Coletânea Python do ZERO às Redes Neurais Artificiais

Geradores e Memória

Como já mencionado algumas vezes ao longo de outros capítulos, devemos sempre prezar pela legibilidade de nosso código e pela performance do mesmo.

Um ponto importante a salientar aqui é que todo e qualquer objeto em

Python ocupará um espaço alocado em memória com tamanho dinamicamente proporcional ao seu conteúdo.

Quando declaramos um determinado tipo de dado manualmente em

Python, o mesmo tende a ter um tamanho diferente de quando declarado automaticamente, isto se dá porque ao especificar manualmente um determinado tipo de dado, implicitamente serão criadas, de acordo com o tipo de dado, alguns métodos para iteração do mesmo.

Em outras palavras, atribuir para uma variável qualquer, por exemplo,

‘Fernando’ e str(‘Fernando’) terão tamanhos diferentes, uma vez que str( ) como iterável carrega em sua estrutura interna alguns métodos ocultos do desenvolvedor, além é claro, dos dados/valores do objeto em si.

O uso de geradores, quando usados para volumes de dados

consideráveis, serve não somente para poupar tempo de codificação mas alocação de memória, pois seus dados serão gerados e alocados conforme necessidade do código.

 

Para que fique mais claro, vamos ao exemplo. Repare que foram criadas duas variáveis lista_manual e lista_gerada, ambas listas com 15 elementos numéricos em sua composição.

 

Por meio da função print( ) parametrizada com sys.getsizeof( ) por sua

vez parametrizada com cada uma das listas, podemos reparar a diferença de tamanho em bytes alocados em memória.

Na primeira linha do retorno temos o valor referente a lista_manual, na

segunda linha, o valor em bytes de lista_gerada.

Aqui temos um exemplo bastante básico, porém já é possível notar que a mesma lista, quando criada via gerador, tem tamanho menor. Raciocine em grandes volumes de dados o quanto esta proporção de tamanho faria a diferença no impacto de performance de seu programa.

 

Apenas como exemplo, também, note o tamanho em bytes de

listamilhão, criada aqui fora de contexto apenas para demonstrar o tamanho de uma expressão de um valor de milhão.

Novamente, procure raciocinar na proporção do espaço de memória

alocado de acordo com o tamanho do conteúdo da respectiva variável.

 

Partindo para uma aplicação real, supondo que depende do contexto de

nosso programa, precisássemos de um gerador de números sequenciais.

Para isso criamos a função gera_numero(), sem parâmetros mesmo.

Dentro de seu corpo criamos a variável num inicialmente com valor zerado, na sequência criamos de fato o gerador para num por meio de yield.

Em outros momentos, como de costume, usávamos de return para retornar um determinado dado/valor para uma variável ao final da execução da função, aqui, no contexto de um gerador, return é substituído por yield, por fim num é atualizada com seu valor acrescido de 1.

Fora da função é criada a variável gerador que chama a função

gera_numero( ).

 

A partir desse momento, temos nosso gerador instanciado e em

execução.

Por meio de nossa função print( ) parametrizada com next( ) por sua vez parametrizada com nosso gerador, a cada execução será gerado um número sequencial, conforme a demanda.

Dessa forma, usaremos apenas o necessário de números e apenas no tempo certo em que o restante do código exigir tal funcionalidade, poupando espaço e processamento.

O retorno, como esperado, foram 5 números sequenciais referentes aos 5

comandos print(next()) usados no exemplo.