6
respostas

Problema com código que deve realizar um sorteio de nomes sem repetições

Terminei meu curso de python e como meu primeiro projeto resolvi fazer um código que importasse uma lista de um arquivo txt e posteriormente sorteasse os vencedores dentre os nomes:

import random

class Sorteador:
def __init__(self, premio, quantidade_por_sorteado, numero_de_sorteados):
        self._premio = premio
        self._quantidade_por_sorteado = quantidade_por_sorteado
        self._numero_de_sorteados = numero_de_sorteados
        self._participantes = []

@property
def premio(self):
    return self._premio

@property
def quantidade_por_sorteado(self):
    return self._quantidade_por_sorteado

@property
def numero_de_sorteados(self):
    return self._numero_de_sorteados

@property
def participantes(self):
    return self._participantes

def __iter__(self):
    return self

def __str__(self):
    return f'os participantes sao {self.carrega_participantes()} e os vencedores sao {self.sorteia_premiados()}'

def carrega_participantes(self):
    arquivo = open("participantes.txt", "r", encoding='utf-8')
    for linha in arquivo:
        linha = linha.strip()
        self.participantes.append(linha)

    arquivo.close()
    return self.participantes

def sorteia_premiados(self):
    sorteados = []
    contador = self.numero_de_sorteados
    while contador > 0:
        indice_sorteado = random.randrange(0, len(self.participantes))
        participante_sorteado = self.participantes[indice_sorteado]
        sorteados.append(participante_sorteado)
        if participante_sorteado in self.participantes:
            del(self.participantes[indice_sorteado])
        contador -= 1

    return sorteados

O inicializador é:

from src import Sorteador


premio = 'Box'
numero_de_sorteados = 2
quantidade_por_sorteado = 2
sorteia = Sorteador(premio, quantidade_por_sorteado, numero_de_sorteados)
print(sorteia, flush=True)

Até aqui o código está funcionando, mas quando tento realizar um laço while ou dar mais de um print:

print(sorteia, flush=True)
print()
print(sorteia, flush=True)

no inicializador a impressão retorna duplicada, da seguinte forma:

os participantes sao ['Marcos', 'Felipe', 'Mario', 'Matheus', 'Francisco', 'Antonio', 'Thiago', 'Igor', 'Paulo', 'Joao'] e os vencedores sao ['Joao', 'Thiago']

os participantes sao ['Marcos', 'Felipe', 'Mario', 'Matheus', 'Francisco', 'Antonio', 'Igor', 'thon310/python.exe c:/UsePaulo', 'Marcos', 'Felipe', 'Mario', 'Matheus', 'Francisco', 'Antonio', 'Thiago', 'Igor', 'Paulo', 'Joao'] e os vencedores sao ['Marcos', 'Paulo']                                             'Igor', 'Paulo', 'Joao']
PS C:\Users\mfsra\Desktop\Projetos\Sorteidor>

Não estou conseguindo entender o problema da duplicação, agradeço desde já qualquer ajudar em solucionar esse código ou sugerir algum que faça o mesmo de forma mais simples. A ideia é que cada sorteado seja único, sem repetições de vencedores.

A lista de participantes salva em um arquivo txt é a seguinte:

Marcos
Felipe
Mario
Matheus
Francisco
Antonio
Thiago
Igor
Paulo
Joao
6 respostas

O problema é que ao carregar o arquivo, você não está zerando a lista self.participantes, então toda vez que você chamar a variável sorteia em um novo print(), ele vai executar o iterator __str__ que vai chamar a api carrega_participantes() que vai carregar os nomes contidos no arquivo na lista self.participantes que já está com conteúdo...

Entendi, mas vc teria uma sugestão de alteração pra corrigir esse problema? Eu real não tenho muita noção do que eu tenha que fazer.

Adiciona self.participantes = []na primeira linha da função que carrega os nomes

Fiz a auteração que você suferiu e não funcionou:

from random import sample, choice
from excecoes import SorteioInvalido


class Sorteia:
    def __init__(self, premio, numero_de_sorteados):
        self._premio = premio
        self._numero_de_sorteados = numero_de_sorteados
        self._participantes = []

    @property
    def premio(self):
        return self._premio

    @property
    def quantidade_por_sorteado(self):
        return self._quantidade_por_sorteado

    @property
    def numero_de_sorteados(self):
        return self._numero_de_sorteados

    @property
    def participantes(self):
        return self._participantes

    def __iter__(self):
        return self

    def __next__(self):
        return self.sorteia_premiados()

    def sorteia_premiados(self):
        participantes = self.carrega_participantes()
        premiados = sample(sample(participantes, len(participantes)), self.numero_de_sorteados)
        for premiado in range(len(premiados)):
            yield premiados[premiado]

    def carrega_participantes(self):
        with open("participantes.txt", "r", encoding='utf-8') as arquivo:
            self.participantes = []
            for linha in arquivo:
                linha = linha.strip()
                self.participantes.append(linha)

        arquivo.close()
        return self.participantes


premio = 'Box'
numero_de_sorteados = 2
sorteador = Sorteia(premio, numero_de_sorteados)

for sorteado in sorteador:
    print(sorteado)

O resultado foi um loop infinito. Tem outra sugestão?

A variável sorteador contém um objeto da classe Sorteia, não uma lista, você alterou parte da source, por exemplo removeu o iterator __str__ que era justamente o que retornava a lista do arquivo e vencedores sorteados quando chamava a variável dentro da api print()

Você pode criar uma função para retornar a lista de vencedores ou participantes e chamar ela usando print(sorteador.FUNCAOCRIADA())

Boa noite, fiz umas alterações no código porem eu não sei dizer se o que eu fiz está de acordo com as boas práticas de programação, ou se teria outra formad e fazer isso. Peço atenção para os métodos especiais Next() e iter():

from random import sample


class Sorteia:
    def __init__(self, premio, numero_de_sorteados):
        self._premio = premio
        self._numero_de_sorteados = numero_de_sorteados

    @property
    def premio(self):
        return self._premio

    @property
    def numero_de_sorteados(self):
        return self._numero_de_sorteados

    def __iter__(self):
        return next(self)

    def __next__(self):
        premiados = self.sorteia_premiados()
        for premiado in range(len(premiados)):
            yield premiados[premiado]

    def carrega_participantes(self):
        with open("participantes.txt", "r", encoding='utf-8') as arquivo:
            participantes = []
            for linha in arquivo:
                linha = linha.strip()
                participantes.append(linha)

        arquivo.close()
        return participantes

    def sorteia_premiados(self):
        participantes = self.carrega_participantes()
        sorteados = sample(sample(participantes, len(participantes)), self.numero_de_sorteados)
        return sorteados


premio = 'Box'
numero_de_sorteados = 2
sorteador = Sorteia(premio, numero_de_sorteados)
for sorteado in sorteador:
    print(sorteado)

Se tiver alguma sugestão pra essa parte do código que indiquei, será muito bem-vinda. Eu não lembro de em algum lugar precisar chamar next(self) no iter, porém foi a unica forma que fez funcionar. O retorno está como eu queria:

Mario
Matheus

A lista a qual ele está puxando os nomes são:

Marcos
Felipe
Mario
Matheus
Francisco
Antonio
Thiago
Igor
Paulo
Joao