Pular para o conteúdo

Como usar a passagem de referência em Python

[

Passagem por referência em Python: Contexto e Melhores Práticas

por Marius Mogyorosi (intermediário) python

Após adquirir familiaridade com Python, você pode perceber casos em que suas funções não modificam os argumentos como o esperado, especialmente se estiver familiarizado com outras linguagens de programação. Algumas linguagens tratam os argumentos de função como referências a variáveis existentes, o que é conhecido como passagem por referência. Outras linguagens os tratam como valores independentes, uma abordagem conhecida como passagem por valor.

Se você é um programador Python intermediário que deseja entender a maneira peculiar do Python de lidar com os argumentos de função, então este tutorial é para você. Você implementará casos de uso reais de construções de passagem por referência em Python e aprenderá várias melhores práticas para evitar armadilhas com seus argumentos de função.

Neste tutorial, você aprenderá:

  • O que significa passagem por referência e por que você gostaria de usá-la.
  • Como a passagem por referência difere tanto da passagem por valor quanto da abordagem única do Python.
  • Como os argumentos de função se comportam em Python.
  • Como você pode usar certos tipos mutáveis para passar por referência em Python.
  • Quais são as melhores práticas para replicar a passagem por referência em Python.

Definindo Passagem por Referência

Antes de mergulharmos nos detalhes técnicos da passagem por referência, é útil dar uma olhada mais de perto no próprio termo, dividindo-o em componentes:

  • Passar significa fornecer um argumento para uma função.
  • Por referência significa que o argumento que você está passando para a função é uma referência a uma variável que já existe em memória, em vez de uma cópia independente dessa variável.

Como você está fornecendo à função uma referência a uma variável existente, todas as operações realizadas nessa referência afetarão diretamente a variável à qual ela se refere. Vamos ver alguns exemplos de como isso funciona na prática.

Abaixo, você verá como passar variáveis por referência em C#. Observe o uso da palavra-chave ref nas linhas destacadas:

C#

using System;
class Program
{
static void Main(string[] args)
{
int arg;
ModifyByReference(ref arg);
Console.WriteLine(arg); // Output: 10
}
static void ModifyByReference(ref int x)
{
x = 10;
}
}

No exemplo acima, a função ModifyByReference recebe uma referência para a variável inteira arg, que é modificada para o valor 10 dentro da função. Essa modificação afeta diretamente a variável arg fora da função ModifyByReference.

Comparando Passagem por Referência e Passagem por Valor

A passagem por referência é diferente da passagem por valor, pois a passagem por referência lida com as referências às variáveis, enquanto a passagem por valor lida com as cópias independentes das variáveis. Vamos comparar os dois métodos:

  • Passagem por Valor: Quando você passa um argumento por valor, uma cópia independente desse argumento é criada. Qualquer modificação feita nessa cópia não afetará o valor original do argumento.
  • Passagem por Referência: Quando você passa um argumento por referência, você está passando uma referência à variável original. Qualquer modificação feita na referência afetará diretamente o valor original da variável.

Em Python, a passagem de argumentos para funções é diferente de ambos os métodos mencionados acima. Python possui seu próprio modo único de tratar os argumentos de função, o qual exploraremos nas seções a seguir.

Utilizando Construções de Passagem por Referência

Agora que você entendeu os conceitos básicos da passagem por referência, vamos explorar algumas maneiras de implementar a passagem por referência em Python.

Evitando Objetos Duplicados

Uma das vantagens de utilizar a passagem por referência é evitar a criação de objetos duplicados. Em Python, quando você passa um objeto mutável como argumento para uma função, você está passando uma referência ao objeto original. Isso significa que qualquer modificação feita na referência dentro da função afetará diretamente o objeto original.

def modify_list(lst):
lst.append(4)
my_list = [1, 2, 3]
modify_list(my_list)
print(my_list) # Output: [1, 2, 3, 4]

No exemplo acima, a função modify_list recebe uma referência à lista my_list e adiciona o número 4 a ela. Como a referência é uma referência direta à lista original, a lista modificada é impressa corretamente.

Retornando Múltiplos Valores

Outra forma de utilizar a passagem por referência em Python é retornando múltiplos valores de uma função. Em vez de retornar os valores separadamente, você pode retornar uma única estrutura de dados que contenha todos os valores desejados.

def calculate_statistics(numbers):
min_value = min(numbers)
max_value = max(numbers)
avg = sum(numbers) / len(numbers)
return {"min": min_value, "max": max_value, "avg": avg}
statistics = calculate_statistics([1, 2, 3, 4, 5])
print(statistics) # Output: {'min': 1, 'max': 5, 'avg': 3}

No exemplo acima, a função calculate_statistics recebe uma lista de números e retorna um dicionário contendo as estatísticas desses números. Ao retornar o dicionário como um todo, é possível acessar todos os valores de estatísticas em um único objeto.

Criando Funções com Múltiplos Retornos Condicionais

Em alguns casos, pode ser útil retornar múltiplos valores de uma função com base em condições específicas. Em vez de criar várias funções para lidar com cada condição, você pode usar a passagem por referência para retornar os valores condicionais diretamente para as variáveis de destino.

def get_user_info(user_id):
user_exists = False
user_data = {}
# Check if user exists in database
if user_id in database:
user_exists = True
user_data = database[user_id]
return user_exists, user_data
id = 123
exists, data = get_user_info(id)
if exists:
print(f"User {id} exists with data:", data)
else:
print(f"User {id} does not exist")

No exemplo acima, a função get_user_info verifica se um usuário existe no banco de dados com base em um ID fornecido. Ela retorna uma variável booleana exists, que indica se o usuário existe ou não, e um dicionário data contendo os dados do usuário. Ao atribuir os retornos da função diretamente às variáveis exists e data, você pode utilizá-las de acordo com as necessidades específicas.

Passando Argumentos em Python

Em Python, a passagem de argumentos para funções segue um processo específico. Para entender melhor, vamos explorar duas partes importantes desse processo: a atribuição em Python e os argumentos de função.

Compreendendo a Atribuição em Python

Em Python, quando você atribui um valor a uma variável, você está criando uma associação entre o nome da variável e o valor. Essa associação é chamada de referência. A variável é apenas um rótulo para o valor e você pode atualizar essa associação, criando uma nova referência para um novo valor.

x = 1
y = x # y referencia o mesmo objeto que x
x = 2 # x agora referencia um novo objeto

No exemplo acima, as variáveis x e y são associadas ao objeto inteiro com valor 1. Ao atribuir y = x, a variável y recebe a mesma referência que a variável x, apontando para o objeto inteiro de valor 1. No entanto, ao modificar o valor de x para 2, uma nova referência é criada para o objeto inteiro com valor 2, enquanto a referência original de y ainda aponta para o objeto com valor 1.

Explorando os Argumentos de Função

Quando você chama uma função em Python, os argumentos passados são tratados de maneira especial. Em vez de apenas copiar os valores, Python cria novas associações entre os nomes dos parâmetros e os argumentos passados. Isso significa que os parâmetros da função são referências para os argumentos, permitindo que a função acesse e modifique, por referência, os valores passados.

def modify_argument(arg):
arg = "modified"
value = "original"
modify_argument(value)
print(value) # Output: original

No exemplo acima, a função modify_argument recebe uma referência para a variável value e tenta modificar o seu valor para “modified”. No entanto, como o argumento arg é uma nova referência para o mesmo objeto que value, a modificação feita dentro da função não afeta a variável original value.

Replicando a Passagem por Referência em Python

Embora Python não tenha suporte nativo para a passagem por referência como alguns outros idiomas, é possível replicar essa funcionalidade usando certas estruturas de dados mutáveis. Aqui estão algumas das melhores práticas para replicar a passagem por referência em Python:

Melhor Prática: Retornar e Atribuir Novamente

Uma forma comum de replicar a passagem por referência em Python é retornar um valor da função e atribuí-lo novamente à variável original.

def modify_list(lst):
lst.append(4)
return lst
my_list = [1, 2, 3]
my_list = modify_list(my_list)
print(my_list) # Output: [1, 2, 3, 4]

No exemplo acima, a função modify_list recebe uma lista my_list, adiciona o número 4 a ela e retorna a lista modificada. Atribuir o retorno da função novamente à variável my_list cria uma nova referência para a lista modificada.

Melhor Prática: Utilizar Atributos de Objeto

Outra abordagem é utilizar os atributos de objetos em Python para replicar a passagem por referência.

class DataContainer:
def __init__(self, data):
self.data = data
def modify_data(container):
container.data += " modified"
my_container = DataContainer("original")
modify_data(my_container)
print(my_container.data) # Output: original modified

No exemplo acima, a classe DataContainer possui um atributo data que é modificado pela função modify_data. Como a classe é mutável, qualquer modificação feita no atributo data dentro da função afeta diretamente o objeto original.

Melhor Prática: Utilizar Dicionários e Listas

Dicionários e listas são tipos mutáveis em Python, o que significa que você pode utilizá-los para replicar a passagem por referência.

def modify_dict(d):
d["new_key"] = "new_value"
my_dict = {"key": "value"}
modify_dict(my_dict)
print(my_dict) # Output: {'key': 'value', 'new_key': 'new_value'}

No exemplo acima, a função modify_dict recebe um dicionário my_dict e adiciona uma nova chave e valor a ele. Como o dicionário é mutável, a modificação é realizada diretamente no dicionário original.

Conclusão

Neste tutorial, você aprendeu sobre a passagem por referência em Python e como ela difere da passagem por valor. Exploramos várias construções de passagem por referência em Python, incluindo evitar objetos duplicados, retornar múltiplos valores, criar funções com múltiplos retornos condicionais e replicar a passagem por referência utilizando melhores práticas. Essas técnicas podem ser úteis para lidar com argumentos de função de forma mais eficiente e evitar surpresas indesejadas. Agora você está pronto para aplicar esses conceitos em suas próprias soluções Python.

Remover anúncios