Pular para o conteúdo

Entenda como o Python passa referência?

CodeMDD.io

Passagem por Referência em Python: Conceito e Melhores Práticas

por Marius Mogyorosi

Após adquirir alguma familiaridade com Python, você pode notar casos em que suas funções não modificam os argumentos no lugar como você esperaria, especialmente se você está familiarizado com outras linguagens de programação. Algumas linguagens tratam os argumentos da 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 forma peculiar do Python de lidar com os argumentos de função, então este tutorial é para você. Você irá implementar casos de uso reais de construções de passagem por referência em Python e aprender várias melhores práticas para evitar problemas com seus argumentos de função.

Definindo Passagem por Referência

Antes de mergulhar nos detalhes técnicos da passagem por referência, é útil analisar mais de perto o próprio termo, que pode ser dividido em componentes:

  • Passagem 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 na memória, e não uma cópia independente dessa variável.

Uma vez que você está dando à 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.

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

using System;
class Program
{
static void Main(string[] args)
{
int arg;
https://codemdd.io/ Passando por referência.
https://codemdd.io/ O valor da variável passada será modificado.
PassByReference(ref arg);
Console.WriteLine(arg); https://codemdd.io/ Output: 10
}
static void PassByReference(ref int x)
{
x = 10;
}
}

Nesse exemplo, a função PassByReference recebe x como um argumento por referência usando a palavra-chave ref. A utilização da palavra-chave ref indica que a variável arg na função Main será modificada diretamente e terá o valor de 10.

Agora vamos comparar a passagem por referência com a passagem por valor.

Comparando Passagem por Referência e Passagem por Valor

A principal diferença entre a passagem por referência e a passagem por valor está na forma como as variáveis são tratadas em relação às funções. Na passagem por referência, a função recebe uma referência à variável existente e todas as modificações afetam diretamente essa variável. Na passagem por valor, a função recebe uma cópia independente da variável e todas as modificações são feitas nessa cópia, não afetando a variável original.

Para entender melhor, vamos comparar os dois métodos de passagem com um exemplo prático em Python:

def pass_by_value(x):
x = 10
def pass_by_reference(obj):
obj.set_value(10)
class MyClass:
def __init__(self, value):
self.value = value
def set_value(self, value):
self.value = value
x = 5
pass_by_value(x)
print(x) # Output: 5
obj = MyClass(5)
pass_by_reference(obj)
print(obj.value) # Output: 10

No exemplo acima, a função pass_by_value recebe x como um argumento e atribui o valor 10 a x. No entanto, essa modificação não afeta a variável original x fora da função, e o valor de x impresso é 5.

Por outro lado, a função pass_by_reference recebe um objeto obj e chama o método set_value para definir o valor como 10. Como a função recebe uma referência ao objeto original, a modificação realmente afeta o objeto original, e o valor de obj.value impresso é 10.

É importante entender a diferença entre passagem por referência e passagem por valor, especialmente ao trabalhar com funções que modificam argumentos. No próximo tópico, exploraremos como os argumentos são passados em Python.

Passando Argumentos em Python

Em Python, é importante entender como a atribuição de variáveis funciona para compreender como os argumentos são passados para as funções.

Compreendendo a Atribuição em Python

Em Python, as variáveis são atribuídas a objetos. Essa atribuição cria uma referência entre a variável e o objeto. Quando uma variável é passada como argumento para uma função, uma cópia da referência é feita, mas não uma cópia do objeto em si. Isso significa que se o objeto for modificado dentro da função, essa modificação afetará a variável original fora da função.

Vamos ver um exemplo para ilustrar isso:

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

Neste exemplo, a função modify_list recebe uma lista lst e adiciona o número 10 a ela. Após chamar a função modify_list com a lista my_list, a lista original é modificada e o valor impresso é [1, 2, 3, 10].

Isso ocorre porque a função modify_list recebe a referência à lista my_list como argumento e modifica a lista diretamente. A atribuição em Python cria uma relação entre a variável lst e a lista original, de modo que qualquer modificação na lista lst é refletida na lista original.

Explorando os Argumentos de Função

Python suporta diferentes tipos de argumentos de função: argumentos posicionais, argumentos nomeados, argumentos posicionais padrão e argumentos de palavras-chave.

  • Argumentos posicionais são passados por ordem e associados aos parâmetros da função.
  • Argumentos nomeados são especificados explicitamente pelo nome ao chamar a função.
  • Argumentos posicionais padrão possuem um valor padrão e podem ser omitidos ao chamar a função.
  • Argumentos de palavras-chave são passados com uma sintaxe especial (nome=valor) e associados aos parâmetros da função pelo nome.

Vamos ver alguns exemplos desses tipos de argumentos:

def greet(name, message="Hello"):
print(f"{message}, {name}!")
greet("Alice") # Output: Hello, Alice!
greet(message="Hi", name="Bob") # Output: Hi, Bob!

No exemplo acima, a função greet recebe dois argumentos: name e message. O argumento name é um argumento posicional, enquanto o argumento message é um argumento posicional padrão. Isso significa que name deve ser o primeiro argumento passado, mas message pode ser omitido, pois possui um valor padrão de "Hello". No segundo chamada da função, os argumentos são especificados explicitamente pelo nome (name="Bob", message="Hi"), permitindo que sejam passados fora de ordem.

Ter uma compreensão clara de como os argumentos de função funcionam em Python é essencial para entender como a passagem por referência pode ser replicada em Python. Vamos explorar isso no próximo tópico.

Replicando Passagem por Referência em Python

Embora Python não suporte a passagem por referência no sentido estrito, é possível replicar esse comportamento com certos tipos de objetos mutáveis, como listas, dicionários e objetos personalizados com atributos mutáveis. Nesta seção, vamos discutir algumas práticas recomendadas para replicar a passagem por referência em Python.

Boas Práticas: Retornar e Reatribuir

Uma das maneiras mais comuns de replicar a passagem por referência em Python é retornar o objeto modificado de uma função e reatribuí-lo à variável original. Isso permite que as modificações feitas na função afetem diretamente o objeto original.

Vamos ver um exemplo:

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

Neste exemplo, a função modify_list recebe uma lista lst, adiciona o número 10 a ela e retorna a lista modificada. Ao chamar a função e reatribuir o resultado à variável my_list, o objeto original é atualizado com as modificações feitas pela função.

Essa abordagem é particularmente útil quando você deseja modificar um objeto dentro de uma função e manter o valor de retorno da função.

Boas Práticas: Usar Atributos de Objeto

Outra maneira de replicar a passagem por referência em Python é usando atributos mutáveis de objetos. Quando você passa um objeto como argumento para uma função e modifica seus atributos mutáveis dentro da função, as modificações afetarão o objeto original.

Vamos ver um exemplo:

class Car:
def __init__(self, color):
self.color = color
def paint_car(car, color):
car.color = color
my_car = Car("blue")
paint_car(my_car, "red")
print(my_car.color) # Output: red

Neste exemplo, a função paint_car recebe um objeto car e uma cor, e atualiza o atributo color do objeto. Ao chamar a função e passar o objeto my_car, o atributo color é atualizado com a nova cor.

Essa abordagem é especialmente útil quando você deseja modificar os atributos de um objeto, como no exemplo acima. No entanto, observe que isso funciona apenas para atributos mutáveis do objeto.

Boas Práticas: Usar Listas e Dicionários

Uma terceira maneira de replicar a passagem por referência em Python é usando listas ou dicionários para encapsular os valores que você deseja modificar. Ao passar a lista ou o dicionário como argumento para a função e modificar seus elementos ou valores dentro da função, as modificações afetarão o objeto original.

Vamos ver um exemplo:

def modify_list(lst):
lst.append(10)
def modify_dict(d):
d["key"] = "value"
my_list = [1, 2, 3]
my_dict = {"key": "old value"}
modify_list(my_list)
modify_dict(my_dict)
print(my_list) # Output: [1, 2, 3, 10]
print(my_dict) # Output: {'key': 'value'}

Neste exemplo, as funções modify_list e modify_dict recebem uma lista lst e um dicionário d, respectivamente, e modificam os elementos ou valores dentro dos objetos. Ao chamar as funções e passar os objetos originais, as modificações afetam diretamente os objetos originais.

Essa abordagem é versátil, pois pode ser usada para modificar qualquer número de elementos ou valores dentro da lista ou do dicionário. Além disso, você também pode passar os objetos por referência dentro da lista ou dicionário, replicando assim a passagem por referência.

Conclusão

Python não suporta a passagem por referência no sentido estrito como algumas outras linguagens de programação, mas é possível replicar esse comportamento com certos tipos de objetos mutáveis. Neste tutorial, você aprendeu a diferença entre passagem por referência e passagem por valor, explorou como os argumentos de função são passados em Python e descobriu algumas práticas recomendadas para replicar a passagem por referência em Python.

Lembre-se de que, ao trabalhar com argumentos de função em Python, é importante entender como a atribuição e a passagem de objetos funcionam para evitar comportamentos indesejados em suas funções. Use as práticas recomendadas apresentadas neste tutorial para gerenciar e modificar seus argumentos adequadamente.


Artigo traduzido e adaptado por Marius Mogyorosi.