Skip to content

Uma explicação rápida sobre side_effect em Python

Updated on

No mundo da programação Python, entender e gerenciar adequadamente os side effects é fundamental. Esses aspectos muitas vezes negligenciados, mas críticos, podem fazer a diferença entre um programa que funciona bem e um programa imprevisível e difícil de manter.

Side effects em Python ocorrem quando uma função, em vez de apenas retornar um valor, também modifica algum estado ou tem uma interação observável com o mundo externo. Embora nem sempre sejam negativos, esses side effects podem introduzir bugs difíceis de rastrear e corrigir. Portanto, aprender a controlá-los ou evitá-los pode melhorar significativamente a estabilidade e confiabilidade do seu código.

Quer criar rapidamente visualizações de dados a partir de um Pandas Dataframe em Python sem escrever código?

PyGWalker é uma biblioteca Python para Análise Exploratória de Dados com Visualização. PyGWalker (opens in a new tab) pode simplificar seu workflow de análise de dados e visualização de dados no Jupyter Notebook, transformando seu dataframe do pandas (e dataframe do polars) em uma interface de usuário no estilo do Tableau para exploração visual.

PyGWalker para visualização de dados (opens in a new tab)

O que é um Side Effect em Python?

Em Python, um side effect é qualquer alteração que uma função faça em seu estado ou no estado global do programa, além de seu valor de retorno. Side effects podem incluir a modificação de uma variável global ou estática, a alteração do objeto original, a produção de saída no console ou a gravação em um arquivo ou banco de dados. Eles são comumente observados em operações como operações de I/O, atualizações de estados globais ou mesmo dentro de métodos de classe que alteram o estado do objeto.

# Exemplo de side effect em Python
def add_element(data, element):
    data.append(element)
    return data
 
my_list = [1, 2, 3]
print(add_element(my_list, 4))  # Saída: [1, 2, 3, 4]
print(my_list)  # Saída: [1, 2, 3, 4]

Nesta função add_element, temos um side effect. A função não apenas retorna a lista atualizada, mas também modifica a lista original my_list. Este é um exemplo típico de side effect em Python e um dos principais erros relacionados a side effect a serem evitados ao escrever código em Python.

Funções Puras em Python

Uma função pura, por outro lado, é uma função que, para a mesma entrada, sempre produz a mesma saída e não tem side effects. Portanto, a pureza de uma função está em sua natureza autocontida, tornando-a previsível e fácil de testar.

# Exemplo de função pura em Python
def add_element_pure(data, element):
    new_list = data.copy()
    new_list.append(element)
    return new_list
 
my_list = [1, 2, 3]
print(add_element_pure(my_list, 4))  # Saída: [1, 2, 3, 4]
print(my_list)  # Saída: [1, 2, 3]

Neste exemplo, add_element_pure é uma função pura. Ela cria uma nova lista com base na entrada e depois adiciona o novo elemento a essa lista. A lista original my_list permanece inalterada. Compreender as características das funções puras em Python é crucial para escrever um código robusto e mais fácil de depurar.

Usando Decoradores para Controlar Side Effects

Os decoradores em Python oferecem uma maneira poderosa de modificar o comportamento de uma função ou classe. Ao envolver a função ou classe, um decorador pode executar código antes ou depois da função envolvida ser executada, sem alterar seu código-fonte.

# Exemplo de decorador em Python
def no_side_effects_decorator(func):
    def wrapper(*args, **kwargs):
        data_copy = args[0].copy()  # criar uma cópia dos dados
        return func(data_copy, *args[1:], **kwargs)
    return wrapper
 
@no_side_effects_decorator
def add_element(data, element):
    data.append(element)
    return data
 
my_list = [1, 2, 3]
print(add_element(my_list, 4))  # Saída: [1, 2, 3, 4]
print(my_list)  # Saída: [1, 2, 3]

No trecho de código acima, o decorador no_side_effects_decorator envolve a função add_element. Ele garante que uma cópia dos dados seja usada para operações, deixando os dados originais intocados. Esta é uma forma de usar decoradores em Python para controlar side effects.

Testando Side Effects Usando Objetos Falsos (Mocks)

A biblioteca unittest.mock em Python é uma ferramenta poderosa para escrever testes. Ela permite que você substitua partes do sistema em teste e faça asserções sobre como elas foram usadas. A biblioteca unittest.mock também fornece uma classe Mock que você pode usar para criar um objeto falso (mock object).

Nos testes de unidade Python, um uso comum de objetos falsos é testar o comportamento do código em condições controladas. Por exemplo, quando uma função chama outra função (que pode ter side effects), um objeto falso pode simular a função chamada, evitando os side effects.

from unittest.mock import Mock
 
# Função original que iremos simular
def add_element(data, element):
    data.append(element)
    return data
 
# Objeto falso (mock object)
add_element = Mock(side_effect=lambda data, element: data + [element])
 
# Teste com o objeto falso
my_list = [1, 2, 3]
print(add_element(my_list, 4))  # Saída: [1, 2, 3, 4]
print(my_list)  # Saída: [1, 2, 3]

Neste exemplo, add_element é uma função que acrescenta um elemento a uma lista, causando um side effect. Mas quando substituímos add_element por um objeto falso, ele simula a função sem o side effect. Isso é um exemplo de uso de objetos falsos em Python para controlar side effects.

A biblioteca unittest.mock também fornece a função patch, que você pode usar para substituir os objetos reais em seu código por instâncias de objetos falsos durante os testes. O 'patch' é desfeito automaticamente após a conclusão do teste.

Na próxima parte do artigo, discutiremos mais sobre a biblioteca patch em Python e exploraremos mais exemplos de objetos falsos em Python. Também mergulharemos mais fundo nas características das funções puras em Python e veremos mais exemplos de decoradores em Python para controlar side effects. Continue acompanhando para aprender como usar o patch em Python e evitar erros comuns ao lidar com side effects em Python.

Usando a Biblioteca Patch em Python

A biblioteca patch em Python, especificamente unittest.mock.patch, permite que você controle o escopo do objeto falso (mock) e determine quando o objeto original é substituído e quando ele é restaurado. É especialmente útil para testes de unidade onde você deseja simular o comportamento de um objeto durante um teste e depois ter o objeto falso removido automaticamente após a conclusão do teste.

from unittest.mock import patch
 
def add_element(data, element):
    data.append(element)
    return data
 
def test_add_element():
    with patch('__main__.add_element', side_effect=lambda data, element: data + [element]):
        my_list = [1, 2, 3]
        print(add_element(my_list, 4))  # Saída: [1, 2, 3, 4]
        print(my_list)  # Saída: [1, 2, 3]
 
test_add_element()

Neste trecho de código, a função add_element é temporariamente substituída dentro do escopo do bloco with. Após a execução da função de teste, a função add_element original é restaurada.

Estruturas de Dados Imutáveis e Side Effects

Estruturas de dados imutáveis são outra ferramenta que ajudam a controlar side effects em Python. Por definição, um objeto imutável não pode ser alterado após ser criado. Em Python, exemplos de estruturas de dados imutáveis incluem tuplas, strings e frozensets.

Ao usar estruturas de dados imutáveis, qualquer operação que modifica os dados irá criar um novo objeto. Isso ajuda a evitar side effects, uma vez que os dados originais permanecem inalterados.

# A tupla em Python é imutável
my_tuple = (1, 2, 3)
new_tuple = my_tuple + (4,)  # Cria uma nova tupla
print(new_tuple)  # Saída: (1, 2, 3, 4)
print(my_tuple)  # Saída: (1, 2, 3)

Neste exemplo, my_tuple é uma tupla, que é uma estrutura de dados imutável em Python. Quando tentamos adicionar um elemento a ela, uma nova tupla new_tuple é criada e my_tuple permanece inalterada.

Conclusão

Gerenciar side effects em Python é um aspecto importante na escrita de código de alta qualidade e fácil manutenção. Ao entender e aproveitar os conceitos de funções puras, decoradores e estruturas de dados imutáveis, além de dominar o uso de objetos falsos (mock) e bibliotecas de patch, você pode tornar seu código Python livre de side effects desnecessários. Isso não apenas torna seu código mais confiável, mas também mais fácil de testar e depurar, resultando em aplicativos Python de maior qualidade.

Perguntas frequentes

Vamos agora responder a algumas perguntas frequentes:

  1. O que é um side effect em Python?
    Um side effect em Python é qualquer alteração que uma função faz no estado do programa ou em seu próprio estado, além do valor que ela retorna. Side effects podem incluir a alteração de variáveis globais ou estáticas, a modificação do objeto original, a produção de saída no console ou a gravação em um arquivo ou banco de dados.

  2. Por que você deve evitar side effects em Python?
    Side effects em Python, embora nem sempre sejam prejudiciais, podem introduzir bugs difíceis de rastrear e corrigir. Eles podem tornar seu código imprevisível e difícil de entender. Ao evitar side effects, você torna seu código mais estável, previsível e mais fácil de testar.

  3. O que é uma função pura em Python?
    Uma função pura em Python é uma função que sempre produz a mesma saída para a mesma entrada e não tem side effects. Funções puras são autocontidas, tornando-as previsíveis e fáceis de testar.