Dúvida sobre implementação na escrita de arquivos

Olá.

Eu não sei se vai ficar bem claro agora e também não queria escrever muito… Caso não fique claro eu faço um exemplo com código mesmo.

No meu código há algumas classes nas quais geram dados. Esses dados são retornados em formato de lista.

Eu queria fazer uma classe(SaveData) que fosse responsável por salvar esses dados em um arquivo ou em arquivos diferentes, caso eu queira. Mas vamos nos atentar ao caso mais simples: salvar em um único arquivo.

Digamos que eu tenha gerado duas listas ( produtos de instâncias de classes diferentes) e queira salvar essas duas listas em (SaveData).

A meu ver, ficaria fácil se eu passasse essas duas listas como parâmetros da classe e cada lista corresponderia a uma coluna nesse arquivo.

Até aí, parece tudo bem. Agora imaginemos que eu queira fazer algo mais dinâmico: essa classe fosse capaz de receber 2 listas, ou 3 listas, ou n listas e conseguisse gerar esse arquivo.

Escrevendo esse post eu tratei que a minha dúvida parece algo como “Passar parâmetros desconhecidos para uma classe”ou algo assim.

Não sei se consegui explicar. Mas é isso: como eu consigo fazer com que uma classe consiga gerenciar uma quantidade desconhecida de parâmetros?

Obrigado.

Depende bastante da linguagem de programação aqui. Os colegas com mais experiência certamente vão esclarecer (ou mesmo corrigir) o meu comentário, mas há vários recursos para fazer o que você deseja. Algumas linguagens suportam vetores ou arrays com tamanho variável, dando diretamente o que você busca; outras listas dentro de listas (como o próprio Python, que você usa em vários exemplos). Seguem então os padrões mais “primitivos”, como listas encadeadas, ou sequência de referências na memória terminadas por uma referência nula (que são mais difíceis de ver fora de C e C++).

1 curtida

Ontem eu estava lendo sobre *args e **kwargs. Daqui a pouco vou fazer meus testes. Obrigado.

Em C eu vejo bastante a utilização de ponteiro de ponteiro nessa situação, mas não masterizei o assunto.

Uma maneira talvez mais simples pode ser transpor os dados. Daí em vez de adicionar colunas, vc adiciona mais linhas no arquivo. Fica bem mais fácil, caso seja possível.

1 curtida

Fiz esse teste e deu certo.

Obrigado a todos.

O que você deveria se perguntar é:

Se existe mais de uma solução, qual vai ser mais fácil dar manutenção no meu código no futuro?

O que é mais fácil de testar, um método que pode receber 10 possíveis combinações de argumentos ou um método que pode receber um número infinito de combinações? Quanto código eu vou ter de escrever para garantir um resultado correto, quanto código vou precisar revisar e ou reescrever quando for mudar esse método?

Ainda por cima, qual vai ser mais complexo de compreender? Qual vai demandar uma documentação mais cuidadosa, não gerarem problemas? Qual tem mais chance de gerar confusão em quem for usar meu código?

Eu realmente desconheço o número de parâmetros?

Pense bem, você realmente desconhece o número de parâmetros ou apenas não definiu bem o seu projeto? Você pode estar fazendo otimização precoce ou sendo preguiçoso e escolhendo o caminho mais fácil no presente que vai custar muito mais caro no futuro sem trazer nenhum benefício a mais no futuro.


Raramente você de fato desconhece o número de parâmetros, apenas nestes casos vale a pena lidar com os problemas de um método com um número arbitrário de parâmetros.

1 curtida

No caso, quem iria escolher isso seria o usuário. Seria oferecidas várias possibilidades para ele escolher. Ele poderia escolher algo que gerasse uma lista de dados, apenas, ou algo que pudesse chamar várias classes que gerariam várias listas.

Não saberia ainda como tratar isso. Saber até sei, mas ficaria mais complexo e não saberia se seria mais fácil de manter no futuro.

É o primeiro projeto que estou fazendo e ainda não consigo enxergar com tanta abrangência.

ao meu ver você está otimizando precocemente e aplicando excesso de engenharia no seu projeto, comece com o conceito mais simples, uma função que recebe uma lista ou um objeto estadoDoJogo… Se realmente você precisar, mude. Mas realmente apenas se precisar.

De resto você deveria realmente sentar e colocar no papel tudo sobre essa funcionalidade, modelar o conceito dessa funcionalidade, restrições, estados possíveis, tudo que realmente é preciso para o usuário chegar no objetivo final e nada mais que isso… mas uma coisa é certa, eu tenho certeza de que uma função com número arbitrário de argumentos nesse caso é uma má ideia.

1 curtida

Eu pensei direito e vi que seria, pelo menos no momento, algo não necessário mesmo. Então deixei com parâmetros fixos.

Agora eu fico imaginando um caso genérico. Como que vocês tratam esse problema? É sempre possível contornar isso?

Quase sempre é. Excesso de casos genéricos na especificação de um projeto é resultado de alguém tentando satisfazer tudo e todos, ou alguém que não sabe claramente o objetivo do projeto, no fim não satisfaz ninguém e não projeta o que esperava.

A melhor lição que alguém pode aprender em programação é que ferramentas tem benefícios e malefícios, sempre, e o maior papel de quem está projetando é encontrar a melhor relação custo benefício para ter um projeto que pode evoluir ou ser corrigido facilmente quando preciso, e esteja o mais sólido possível nos lançamentos.

Parte disso também inclui gerenciar os recursos, quantia de desenvolvedores trabalhando no projeto por hora, etc e como isso afeta o que pode ser incluído no projeto na primeira versão, o que deve ser cortado para uma futura iteração ou simplesmente deve ser abandonado (não é viável).


Não significa que não devamos usar *args e **kwargs nunca, apenas devemos usar quando realmente for a melhor solução. Digamos que eu queira uma forma prática de observar métodos e suas performances, de qualquer método no meu programa.

Eu poderia modificar todos os métodos, mas isso levaria a uma quantia enorme de código redundante poluindo todos eles, isso seria ruim, além do retrabalho de modificar todos os métodos, sem contar as chances de inserir erros em centenas de métodos.

Decorators e *args e **kwargs nesse caso são a solução, porque os benefícios superam os pontos negativos:

import time

def performance(function):
    def wrapper(*args, **kwargs):
        print(f'{function.__name__} quer {function.__code__.co_varnames}')
        print(f'{function.__name__} recebeu {*args, *kwargs}')
        start = time.time()
        resultado = function(*args, **kwargs)
        end = time.time()
        print(f'{function.__name__} retornou {resultado} em {end - start} segundos')
        return resultado
    return wrapper


@performance
def meu_metodo(a, b, c) :
    return a + b / c
   
    
meu_metodo(12, 22, 6)