[파이썬] 파이썬 기초- 조 편성 프로그램 만들기
도움되는 글
[파이썬] 파이썬 기초 - 랜덤 모듈
이전 내용 [파이썬] 파이썬 기초: 함수 (보완)파이썬 함수 (다른 글) [파이썬] 파이썬기초: 함수시작에 앞서해당 내용은 ' 박응용 지음. 이지스 퍼블리싱' 을 토대로 작성되었습니다. 보다 자세한
puppy-foot-it.tistory.com
수행 내용
학원에서 곧 있을 조별 과제를 앞두고 조 편성을 어떻게 하게 될지 궁금했다.
그런데, 교수님께서 조 편성을 하게 되면 학생들은 이런저런 이유를 대며 교수님의 결정을 받아들이려 하지 않을수도 있다고 생각했다. 그럴때 가장 좋은 게 가위바위보, 엎어라뒤집어라 (지방마다 다르다) 등등 여러 가지 방법들이 많이 있으나, 프로그래밍 언어를 배우는 입장에서 이 역시 프로그램으로 구현해 보면 좋을 거 같다는 생각이 들었다.
또한, 학교, 기관을 비롯한 많은 단체들에서 조 편성을 해야 하는 경우들이 많이 있는데, 프로그램이 랜덤으로 짜주게 되면 조 편성 관련해서 큰 스트레스가 사라지지 않을까 하는 마음에 실행 프로그램으로 제작하여 배포하고자 한다.
◆ 우선 필요한 내용 (변수)은
- 전체 인원 수: 조 편성을 위해 전체 몇 명이 필요한 지를 알아야 한다. 그러나 이는 그때그때 달라질 수 있으므로, 입력값을 받도록 한다
- 편성할 그룹 수: 인원에 따라 편성되는 그룹의 수가 달라질 수 있으므로, 이 역시 입력값을 받도록 한다.
- 각 그룹에 속하게 되는 인원은 숫자로 표현 된다. 실생활에서는 제비뽑기나 사다리게임 처럼 번호를 미리 정하고나서 프로그램을 돌려서 본인 번호에 맞는 번호를 찾아가면 될 듯 하다.
1차 내용
기존에 작성했던 로또 프로그램 관련 글과 랜덤 모듈 관련 글을 참고하여 (파이썬 프로그래밍 배울 때만 해도 코드 다 안 보고 썼는데, 요즘 넘파이, 판다스, 머신러닝 배우면서 다시 원점으로 돌아간 듯 하다.... 코드 기계처럼 코드 주루루룩 쓰던 너 2주 만에 어딜 간거냐..)
from random import sample
import random
people= int(input("인원을 입력해 주세요 (숫자만): "))
groups = int(input("편성할 조 숫자를 입력해 주세요 (숫자만): "))
numbers = range(1, people) # 사람 번호
people_each_group = int(people/groups) # 한 그룹당 인원 수
if (people <= 0)| (groups <= 0):
print("1 이상의 숫자를 입력해 주세요.")
else:
for group in range(groups):
project_group = sample(numbers, people_each_group)
print(project_group)
각설하고, 코드를 대략적으로 보면
- people 변수: 전체 인원을 나타내는 변수로써, 앞서 언급했듯 input 값을 받도록 했다. (해당 값이 정수형으로 되도록 int 함수 적용)
- groups 변수: 편성할 그룹의 숫자를 나타내는 변수로써, people 변수와 비슷하다.
- numbers 변수: 사람을 나타내는 변수로써, 생성되는 그룹 리스트 내에 담기게 된다.
- people_each_group: 각 그룹 당 인원 수를 나타내는 변수로써, 전체 인원을 그룹 수로 나눈 값.
- if 조건문: people 변수나 groups 변수가 0보다 작다면, 1 이상의 숫자를 입력해 달라고 하고, 둘 다 1 이상의 숫자가 입력되면 반복문이 실행된다.
- for 반복문: 편성할 그룹의 수 만큼 반복문이 돌면서 project_group 이라는 변수 안에 numbers를 각 그룹 당 인원 수만큼 랜덤으로 뽑아내고, 각 그룹에 속하는 번호(numbers)를 출력하며 조편성이 마무리 된다.
이를 실행해보면
▶ 그런데, 문제가 있다. 3번과 9번 학생의 경우 1조에도 속하고, 3조에도 속하게 된다. 한 명이 두 개 이상의 그룹에 속하지 못하도록 수정을 해줘야 할 필요가 있다.
1차 수정
중복되지 않도록 하기
앞서 얘기한 한 사람이 두 개 이상의 그룹에 속하는 것을 방지하기 위한 코드로 수정했다.
from random import sample
import random
people= int(input("인원을 입력해 주세요 (숫자만): "))
groups = int(input("편성할 조 숫자를 입력해 주세요 (숫자만): "))
numbers = list(range(1, people + 1)) # 사람 번호 # 중복 제거를 위해 리스트로 생성
people_each_group = int(people/groups) # 한 그룹당 인원 수
if (people <= 0)| (groups <= 0):
print("1 이상의 숫자를 입력해 주세요.")
else:
for group in range(groups):
project_group = sample(numbers, people_each_group)
for person in project_group:
numbers.remove(person) # 선택된 사람 번호 삭제
print(f'{group+1}조: ', project_group)
print('조 편성이 완료되었습니다.')
- numbers 변수를 리스트 형태로 받도록 하였고, for 반복문을 중첩 반복문으로 바꿔서 numbers에서 선택된 사람은 제외되도록 numbers.remove 부분을 추가해줬다. (이 명령어는 조가 정해질 때마다, 선택된 사람 번호를 numbers 리스트에서 삭제한다.)
- numbers 변수에서 range는 범위 생성 시 뒤의 숫자를 포함하지 않으므로, 1을 더하여 인원 수 만큼 범위가 생성되도록 했다.
- 그리고 본인이 몇 조인지 알기 쉽도록 project_group 변수 print 문에 각 조가 표시되도록 했다.
다시 코드를 실행해보면,
만약에, 11명이거나 17명처럼 숫자가 소수라서 정확히 떨어지지 않을 경우에는 어떻게 해야 할까?
그 부분도 반영이 되도록 해줘야 할 거 같다. (개인적으로 필자는 이런 디벨롭 과정이 너무 재밌다.)
2차 수정
전체 인원과 그룹 수가 딱 떨어지지 않는 경우 보완하기
인원이 맞지 않는 상황을 대비하여 코드를 다시 수정하였다.
from random import sample
import random
people= int(input("인원을 입력해 주세요 (숫자만): "))
groups = int(input("편성할 조 숫자를 입력해 주세요 (숫자만): "))
numbers = list(range(1, people + 1)) # 사람 번호 # 중복 제거를 위해 리스트로 생성
if (people <= 0)| (groups <= 0): # 0 이하의 숫자를 입력했을 경우
print("1 이상의 숫자를 입력해 주세요.")
else:
people_each_group = int(people/groups) # 한 그룹당 인원 수
extra_people = people % groups # 나머지 인원 수
project_groups = []
for group in range(groups):
if group < extra_people: # 여분의 인원이 있는 그룹에 추가
group_size = people_each_group + 1
else:
group_size = people_each_group
project_group = sample(numbers, group_size)
for person in project_group:
numbers.remove(person) # 선택된 사람 번호 삭제
project_groups.append(project_group)
print(f'{group+1}조: ', project_group)
print('조 편성이 완료되었습니다.')
참고했던 내용
https://puppy-foot-it.tistory.com/645
- extra_people 변수는 people 을 groups로 나눈 후 나머지가 대입되도록 하였고,
- project_groups 는 반복문이 실행되면서 리스트에 값이 추가되기 위해 빈 리스트 형태로 생성하였다.
- 만약에 group 보다 extra_people이 큰 경우 (그룹 수와 인원이 맞지 않는 경우), 한 그룹 당 인원 수가 한 명 더 들어가도록 수정하고 이를 group_size 라는 변수에 대입되도록 하였다.
- 수가 맞을 경우에는 group_size 변수에는 전체 인원이 각 그룹에 고루 들어가도록 하였다.
- project_group 변수는 numbers에서 group_size의 수 만큼 랜덤으로 뽑고, 선택된 사람 번호는 삭제 되도록 remove 를 적용하였다.
이렇게 코드를 수정한 뒤 실행해보면
원하는 대로, 숫자가 맞지 않을 경우 남은 수만큼 조의 순서대로 대입되도록 하였다. (물론, 임의의 조에 대입되도록 할 수도 있지만, 그거는 추후에 여유가 되면 해보도록.)
오늘 학원에서 반장 선출 이벤트가 있었는데, 대부분 반장이 되는 것을 벌칙(!) 처럼 여겨서 내가 교수님께 우리가 프로그래밍을 배우니, 프로그래밍을 이용해서 한 명을 랜덤으로 뽑는 것은 어떠냐는 제의를 했다. (물론, 교수님께 반려 당하긴 했다.)
이렇게 만들어보고 나니, 이 프로그램에 조편성 뿐 아니라 이벤트(벌칙이든, 상품이든) 당첨자 발표 프로그램, 그리고 특정 인원 수 뽑기 + 등수 부여하기 (각 등 수에 따라 상품 또는 벌칙 차등화를 위함) 등의 기능도 다 넣어서 추첨 프로그램을 좀 더 빌드업 시켜보면 어떨까 하는 생각이 들었다.
앞서 배웠던 클래스 개념을 도입해서 좀 더 디벨롭 시켜보면 좋을 거 같다.
3차 수정
다른 추첨의 경우도 넣어서 추첨 프로그램을 다양화 시켜주기(feat. 클래스)
앞서 언급한 대로, 조편성 기능 뿐 아니라, 당첨자 추첨 및 당첨 순위 부여 기능이 추가된 프로그램을 만들었다.
Lot_program 클래스는 프로그램의 기능이 담겨 있으며, 부모 클래스이다.
Start 클래스는 Lot_program 클래스를 상속한 자식 클래스로, 기능을 선택할 수 있고, 앞서 Lot_program에 만들어둔 기능(함수)들이 잘 실행될 수 있도록 하는 역할을 하고 있다.
from random import sample
import random
import os
class Lot_program():
def __init__(self): # 기본
pass
def group_formation(self): # 조 편성 프로그램램
people= int(input("인원을 입력해 주세요 (숫자만): "))
groups = int(input("편성할 조 숫자를 입력해 주세요 (숫자만): "))
numbers = list(range(1, people + 1)) # 사람 번호 # 중복 제거를 위해 리스트로 생성
if (people <= 0)| (groups <= 0): # 0 이하의 숫자를 입력했을 경우
print("1 이상의 숫자를 입력해 주세요.")
else:
people_each_group = int(people/groups) # 한 그룹당 인원 수
extra_people = people % groups # 나머지 인원 수
project_groups = []
for group in range(groups):
if group < extra_people: # 여분의 인원이 있는 그룹에 추가
group_size = people_each_group + 1
else:
group_size = people_each_group
project_group = sample(numbers, group_size)
for person in project_group:
numbers.remove(person) # 선택된 사람 번호 삭제
project_groups.append(project_group)
print(f'{group+1}조: ', project_group)
print('-' * 25)
print('조 편성이 완료되었습니다.')
def lot_program(self): # 당첨 프로그램
people= int(input("인원을 입력해 주세요 (숫자만): "))
winners = int(input("당첨 인원을 입력해 주세요 (숫자만): "))
numbers = list(range(1, people + 1)) # 사람 번호
if (people <= 0)| (winners <= 0): # 0 이하의 숫자를 입력했을 경우
print("1 이상의 숫자를 입력해 주세요.")
else:
winner_members = sample(numbers, winners)
print("당첨을 축하합니다.")
print('-' * 25)
print("당첨 멤버: ", winner_members)
def lot_with_rank(self): # 순위 당첨
people= int(input("인원을 입력해 주세요 (숫자만): "))
winners = int(input("당첨 인원을 입력해 주세요 (숫자만): "))
numbers = list(range(1, people + 1)) # 사람 번호
if (people <= 0)| (winners <= 0): # 0 이하의 숫자를 입력했을 경우
print("1 이상의 숫자를 입력해 주세요.")
else:
for winner in range(winners):
winner_member = sample(numbers, 1)
for person in winner_member:
numbers.remove(person) # 중복 제거
print(f"{winner+1}등: ", winner_member)
print('-' * 25)
print("추첨이 완료되었습니다.")
class Start(Lot_program):
def display_menu(self):
print("실행할 프로그램 선택")
print('-' * 25)
print("1. 조 편성 프로그램")
print("2. 당첨 프로그램")
print("3. 당첨 프로그램 - 순위 있음")
print("4. 종료")
menu = int(input("메뉴를 선택해 주세요: "))
return menu
def main(self):
while True:
try:
menu = self.display_menu()
if menu == 1:
self.group_formation()
if menu == 2:
self.lot_program()
if menu == 3:
self.lot_with_rank()
if menu == 4:
print("프로그램을 종료합니다.")
break
except ValueError:
print("잘못된 숫자를 입력하였습니다.")
finally:
print('-' * 20)
print("프로그램을 이용해 주셔서 감사합니다.")
# 인스턴스 생성
game_1 = Start()
game_1.main()
[참조한 내용]
https://puppy-foot-it.tistory.com/479
https://puppy-foot-it.tistory.com/659
https://puppy-foot-it.tistory.com/658
https://puppy-foot-it.tistory.com/660
이 코드를 실행해보면
잘 실행되는 것을 확인할 수 있다.
이제 이를 실행 프로그램 (exe 파일)로 만들어서 업로드하는 것만 남았다.
4차 수정
.py 확장자 파일 exe 파일로 변환하기
py 파일을 exe 파일로 만들기 위해서
- cmd에 pip install pyinstaller 실행하여 pyinstaller 다운 받기
- pytinstaller 설치 후, cd 명령어를 사용해서 해당 py 파일이 설치된 경로로 이동
- py 파일을 exe 파일로 만들어주는 명령어 (하단) 입력하기
pyinstaller --onefile lot_program.py
Build complete! 라는 내용이 뜨면, exe 파일로 변환이 완료되었다는 뜻이며,
exe 파일이 설치된 폴더로 들어가보면
[build] [dist] 라는 두 개의 폴더가 생성되어있다.
◆ 그렇다면 build 폴더와 dist 폴더는 각각 어떤 역할을 할까?
1. build 폴더
- 역할: build 폴더는 PyInstaller가 .exe 파일을 생성하는 중간 과정에서 필요한 임시 파일들이 저장되는 곳으로, 이 폴더에는 빌드 과정에서 사용된 설정 정보와 .exe 파일을 만드는 데 필요한 임시 파일들이 포함된다.
- 내용: 이 폴더에는 여러 빌드 관련 파일들이 들어있으며, 빌드 로그나 캐시 파일도 여기에 저장된다. 하지만 최종 실행 파일을 실행할 때는 이 폴더는 필요하지 않으며, 배포 시에도 무시해도 된다.
2. dist 폴더
- 역할: dist 폴더는 최종 실행 파일(.exe)을 포함하는 폴더로, 이 폴더 안에 변환된 .exe 파일이 위치하며, 이 파일이 실행 가능한 최종 파일이다.
- 내용: dist 폴더에는 변환된 실행 파일뿐만 아니라 그 파일이 실행되기 위해 필요한 다른 모든 파일들도 함께 포함될 수 있다. 예를 들어, --onefile 옵션을 사용하면 하나의 단일 .exe 파일로 모든 것이 압축되어 들어갑니다
이 중, dist 폴더에 들어가면
exe 프로그램이 있고, 이를 실행하면
켜지기는 하는데, 잘 실행이 될까?
다행히도, 실행이 잘 된다.
[4번] 종료를 누르면 프로그램이 종료된다.
[참고한 글]
https://puppy-foot-it.tistory.com/289
파일 공유