Executando verificação de segurança...
6

[Dúvida] [Python] Como evitar a repetição de parâmetros em várias funções.

Em um código Python, se várias funções recebem os mesmos parâmetros (especialmente se forem muitos), existe alguma boa prática para evitar repetição e melhorar a organização?

Por exemplo nesse código:

def calcular_imposto(valor_bruto, aliquota, deducoes, dependentes, estado):

def calcular_salario_liquido(valor_bruto, aliquota, deducoes, dependentes, estado):

def gerar_holerite(valor_bruto, aliquota, deducoes, dependentes, estado):

Imagine uma dezena ou mais de funções assim, todas com muitos parametros e todos se repetindo por serem usados em todas as funções. Existe alguma forma de evitar a repetição?

8

Tem algumas opções como usar a lib dataclass para gerar um placeholder para esses valores, então tu conseguiria passar o placeholder ao invês de uma repetição.

from dataclasses import dataclass

@dataclass
class ParametrosFinanceiros:
    valor_bruto: float
    aliquota: float
    deducoes: float
    dependentes: int
    estado: str

def calcular_imposto(params: ParametrosFinanceiros):
    # use params.valor_bruto, params.aliquota, etc.
    pass

def calcular_salario_liquido(params: ParametrosFinanceiros):
    pass

def gerar_holerite(params: ParametrosFinanceiros):
    pass

# Uso
parametros = ParametrosFinanceiros(5000, 0.2, 300, 2, 'SP')
calcular_imposto(parametros)
calcular_salario_liquido(parametros)
gerar_holerite(parametros)
0
6

Sem modificar as funções, uma alternativa é colocar todos os valores em uma tupla, e depois usar a sintaxe de unpacking ao chamá-las:

params = (valor_bruto, aliquota, deducoes, dependentes, estado)
 
calcular_imposto(*params)
calcular_salario_liquido(*params)
gerar_holerite(*params)

Repare no asterisco antes de params, é ele que faz o unpacking: o primeiro elemento da tupla é passado como o primeiro argumento da função, o segundo elemento é passado como o segundo argumento e assim por diante.


Claro que tem outras soluções mais rebuscadas, como criar uma classe que contém todos os valores, aí as funções só recebem a instância da classe. Mas aí precisa avaliar se justifica aumentar a complexidade.

Por exemplo, se não puder mudar as funções, a solução com unpacking me parece mais interessante.

0
4

Quando os parâmetros se repetem demais, talvez é uma indicação de que eles pertencem ao escopo de uma classe, inclusive as funções.

Uma solução é a que foi dada pelo KitsuneSemCalda, mas você pode incluir os métodos também:

Assim, as chamadas não precisam mais repetir a expressão "parametros"

(PS: Mudei o nome da classe para CalculosFinanceiros, porque faz mais sentido nesse escopo)

from dataclasses import dataclass

@dataclass
class CalculosFinanceiros:
    valor_bruto: float
    aliquota: float
    deducoes: float
    dependentes: int
    estado: str

    def calcular_imposto(self):
        # use self.valor_bruto, self.aliquota, etc.
        pass
    
    def calcular_salario_liquido(self):
        pass
    
    def gerar_holerite(self):
        pass

# Uso
calculos = CalculosFinanceiros(5000, 0.2, 300, 2, 'SP')
calculos.calcular_imposto()
calculos.calcular_salario_liquido()
calculos.gerar_holerite()
-4

Salve Andy!

Não conheço nenhum artifício da linguagem pra omitir esses parâmetros repetidos, mas também acredito que caso exista esse artifício isso só dificultaria a legibilidade do código.

Hoje em dia as IDEs possuem auto-complete, o que facilita bastante. Caso você queira reduzir seu código você pode passar como argumento um dicionário (não é uma boa opção e é mais trabalhoso na hora de chamar a função, mas solucionaria teu problema tendo que passar somente 1 variável)

def sum(nums):
    return (nums['a'] + nums['b'])
    
nums = {'a': 5, 'b': 7}

print(f'Sum of {nums["a"]} and {nums["b"]} is {sum(nums)}')
1

Passar dicionários como argumentos para resolver esse problema; ao meu ver é a melhor forma. Utilizar encapsulamento quando há um número alto ou padrões de dados conhecidos ou repetidos é uma boa prática de programação, isso proporciona um código mais fácil de ler, manutenível e menos propenso à erros, é algo que você aprende ao trabalhar com linguagens com paradigma de OO. Ao citar que passar um dicionário não é uma boa opção, fez você receber esses downvotes.

2
2

Vale lembrar que, caso não seja possível alterar as funções, ainda dá pra passar um dicionário:

def calcular_imposto(valor_bruto, aliquota, deducoes, dependentes, estado):
    # faz algo com os valores...

def calcular_salario_liquido(valor_bruto, aliquota, deducoes, dependentes, estado):
    # faz algo com os valores...
# mais trocentas funções com os mesmos parâmetros...

params = { 'valor_bruto': 10, 'aliquota': 20, 'deducoes': 3, 'dependentes': 4, 'estado': 'SP' }
calcular_imposto(**params)
calcular_salario_liquido(**params)

O que faz a "mágica" são os dois asteriscos antes de params: eles fazem o unpacking do dicionário. No exemplo acima, ele faz com que o parâmetro valor_bruto receba o valor 10, aliquota receba o valor 20 e assim por diante.

Claro que se vc já tiver os valores em variáveis separadas, talvez não compense criar o dicionário. Neste caso eu ia preferir usar uma das soluções acima: a tupla que eu sugeri (caso não possa mudar as funções) ou uma classe (mas aí teria que alterar todas as funções, e dependendo do caso pode valer a pena).

De qualquer forma, esta é uma das possibilidades, mas acho que só seria "melhor" caso o dicionário já estivesse criado (por exemplo, se ele veio de algum lugar que já retorna um JSON com todos os valores).