DINO NET
(23.02.01) 실습 본문
본 게시물은 코드잇 python 토픽에 나온 실습을 사용하였습니다.
실습 1 - 게임 캐릭터 만들기
조건:
- 인스턴스 변수(타입)
- name(문자열): 캐릭터의 이름
- hp(숫자형): 캐릭터의 체력
- power(숫자형): 캐릭터의 공격력
- 인스턴스 메소드
- __init__: 사용할 모든 인스턴스 변수를 설정한다.
- is_alive: 게임 캐릭터의 체력이 0보다 큰지(살았는지 죽었는지) 확인한다.
- 0 초과이면 True를, 0 이하라면 False를 리턴한다.
- get_attacked: 게임 캐릭터의 체력이 0보다 큰 상태라면 파라미터로 받은 공격력만큼 체력을 깎는다.
- 조건:
- is_alive 메소드를 사용해서 인스턴스가 살아있을 때만 체력을 깎는다. 이미 캐릭터가 죽었으면 죽었다는 메시지를 출력한다.
- 남은 체력보다 공격력이 더 크면 체력(hp)을 0으로 설정한다.
- 조건:
- attack: 파라미터로 받은 다른 캐릭터의 체력을 자신의 공격력만큼 깎는다.
- 조건:
- is_alive 메소드를 이용해서 살아있는 인스턴스만 공격을 할 수 있도록 한다.
- get_attacked 메소드를 사용한다.
- 조건:
- __str__: 게임 캐릭터의 의미있는 정보를 포함한 문자열을 리턴한다.
출력예시
Ww영훈전사wW님은 이미 죽었습니다.
Ww영훈전사wW님의 hp는 0만큼 남았습니다.
Xx지웅최고xX님의 hp는 70만큼 남았습니다.
실습 결과
class GameCharacter:
# 게임 캐릭터 클래스
def __init__(self, name, hp, power):
# 게임 캐릭터는 속성으로 이름, hp, 공격력을 갖는다
self.name = name
self.hp = hp
self.power = power
def is_alive(self):
# 게임 캐릭터가 살아있는지(체력이 0이 넘는지) 확인하는 메소드
return self.hp > 0
def get_attacked(self, damage):
"""
게임 캐릭터가 살아있으면 공격한 캐릭터의 공격력만큼 체력을 깎는 메소드
조건:
1. 이미 캐릭터가 죽었으면 죽었다는 메시지를 출력한다
2. 남은 체력보다 공격력이 더 크면 체력은 0이 된다.
"""
if self.is_alive():
self.hp = 0 if self.hp <= damage else self.hp - damage
else:
print("{}님은 이미 죽었습니다.".format(self.name))
def attack(self, other_character):
# 게임 캐릭터가 살아있으면 파라미터로 받은 다른 캐릭터의 체력을 자신의 공격력만큼 깎는다
other_character.get_attacked(self.power)
def __str__(self):
# 게임 캐릭터의 의미있는 정보를 포함한 문자열을 리턴한다
return "{}님의 hp는 {}만큼 남았습니다.".format(self.name, self.hp)
# 게임 캐릭터 인스턴스 생성
character_1 = GameCharacter("Ww영훈전사wW", 200, 30)
character_2 = GameCharacter("Xx지웅최고xX", 100, 50)
# 게임 캐릭터 인스턴스들 서로 공격
character_1.attack(character_2)
character_2.attack(character_1)
character_2.attack(character_1)
character_2.attack(character_1)
character_2.attack(character_1)
character_2.attack(character_1)
# 게임 캐릭터 인스턴스 출력
print(character_1)
print(character_2)
설명
객체 character
속성 (__init__에서 정의) -> name, hp, power
행동 (method로 정의) -> is_alive, attack, get_attacked
def __init__(self, name, hp, power):
#속성 정의
self.name = name
self.hp = hp
self.power = power
1. is_alive(self): 살아있는가?
살아있다 == (self.hp > 0) / 죽었다 == (self.hp <= 0)
boolean을 이용하여 self.hp가 0을 초과하는지 그 이하인지 판별하면 됨.
def is_alive(self):
return self.hp > 0
#self.hp > 0 이면 True 출력 (살아있음)
#self.hp < 0 이거나 self.hp == 0 이면 False 출력 (죽었음)
2. attack(self, other_character)과 get_attacked(self, damage)
공격한다 == other_character.hp - self.power
공격을 받다 == self.hp - damage
-> 위 실습에서는 attack메소드만 부르므로 attack내부에서 get_attacked을 사용.
def attack(self, other_character):
other_character.get_attacked(self.power)
#get_attacked의 주체가 other_character이므로 damage == self.power
※조건(get_attacked 기준)※
1. is_alive 메소드를 사용해서 인스턴스가 살아있을 때만 체력을 깎는다. 이미 캐릭터가 죽었으면 죽었다는 메시지를 출력한다. -> self.hp <= 0 일 때, print("{}님이 죽었습니다.".format(self.name))
2. 남은 체력보다 공격력이 더 크면 체력(hp)을 0으로 설정한다. -> self.hp <= damage 일 때, self.hp = 0
-> self.hp 값이 중요하므로 is_alive를 사용하여 if else로 조건에 맞춰 나누기
-> 살아있는 경우 self.hp와 damage값을 비교하여 self.hp의 값을 설정 (ternary 구문 사용)
* ternary expression
true_value if condition else false_value
def get_attacked(self, damage):
if self.is_alive():
#살아있음
self.hp = 0 if self.hp <= damage else self.hp - damage
#ternary expression
else:
#죽었음
print("{}님은 이미 죽었습니다.".format(self.name))
__str__이용하여 유의미한 정보 출력 -> character의 hp 출력
def __str__(self):
return "{}님의 hp는 {}만큼 남았습니다.".format(self.name, self.hp)
# self.name + "님의 hp는 " + self.hp + "만큼 남았습니다."
실습 2 - 블로그 유저 만들기
조건:
post 클래스
class Post:
# 게시글 클래스
def __init__(self, date, content):
# 게시글은 속성으로 작성 날짜와 내용을 갖는다
self.date = date
self.content = content
def __str__(self):
# 게시글의 정보를 문자열로 리턴하는 메소드
return "게시일: {}\n내용: {}".format(self.date, self.content)
- 인스턴스 변수(타입)
- name(문자열): 블로그 사용자의 이름
- posts(리스트): 블로그 게시글들을 담을 리스트
- 메소드
- __init__: 인스턴스 변수가 설정되는 메소드
- add_post: 블로그 사용자의 블로그 게시글 리스트에 새로운 게시글 인스턴스를 추가하는 메소드
- show_all_posts: 블로그 사용자가 올린 모든 게시글을 출력하는 메소드
- __str__: 블로그 사용자의 간단한 인사와 이름을 문자열로 리턴하는 메소드
출력예시
안녕하세요 성태호입니다.
작성 날짜: 2019년 8월 30일
내용:
오늘은 내 생일이었다.
많은 사람들이 축하해줬다.
행복했다.
작성 날짜: 2019년 8월 31일
내용:
재밌는 코딩 교육 사이트를 찾았다.
코드잇이란 곳인데 최고다.
같이 공부하실 분들은 www.codeit.kr로 오세요!
실습 결과
class Post:
# 게시글 클래스
def __init__(self, date, content):
# 게시글은 속성으로 작성 날짜와 내용을 갖는다
self.date = date
self.content = content
def __str__(self):
# 게시글의 정보를 문자열로 리턴하는 메소드
return "작성 날짜: {}\n내용: {}".format(self.date, self.content)
class BlogUser:
# 블로그 유저 클래스
def __init__(self, name):
"""
블로그 유저는 속성으로 이름, 게시글들을 갖는다
posts는 빈 배열로 초기화한다
"""
self.name = name
self.post = []
def add_post(self, date, content):
# 새로운 게시글 추가
new_post = Post(date, content)
self.post.append(new_post)
def show_all_posts(self):
# 블로그 유저의 모든 게시글 출력
for post in self.posts:
print(post)
def __str__(self):
# 간단한 인사와 이름을 문자열로 리턴
return "안녕하세요 {}입니다.\n".format(self.name)
# 블로그 유저 인스턴스 생성
blog_user_1 = BlogUser("성태호")
# 블로그 유저 인스턴스 출력(인사, 이름)
print(blog_user_1)
# 블로그 유저 게시글 2개 추가
blog_user_1.add_post("2019년 8월 30일", """
오늘은 내 생일이었다.
많은 사람들이 축하해줬다.
행복했다.
""")
blog_user_1.add_post("2019년 8월 31일", """
재밌는 코딩 교육 사이트를 찾았다.
코드잇이란 곳인데 최고다.
같이 공부하실 분들은 www.codeit.kr로 오세요!
""")
# 블로그 유저의 모든 게시글 출력
blog_user_1.show_all_posts()
설명
객체 Post
속성(__init__) -> date, content
객체 BlogUser
속성(__init__) -> name, posts
행동(method) -> add_post, show_all_posts
def __init__(self, name):
self.name = name
self.post = []
#포스트 리스트, 각 인스턴스마다 할당됨
1. add_post(self, date, content)
이 블로그에서 공통적으로 post에는 date라는 값과 content라는 값이 할당됨
이는 Post 클래스를 사용하여 게시물 인스턴스를 만들어내면 됨!
그리고 만들어진 게시물 객체를 블로그 유저의 포스트 리스트에 추가하면 된다.
def add_post(self, date, content):
#새로운 포스트 객체 생성
new_post = Post(date, content)
self.post.append(new_post)
#블로그 유저 내 post리스트에 추가
2. show_all_posts(self)
post 리스트에 저장되어있는 게시물들을 내보내기.
for문을 이용하여 리스트에 저장되어있는 모든 값들을 이끌어내면 된다.
※ 각 인스턴스들은 Post란 클래스로 만들어졌으므로 pirnt를 이용하면 자동적으로 "안녕하세요 {}입니다.\n".format(self.name) 값 리턴
def show_all_posts(self):
for post in self.post:
print(post)
#print를 이용하여 __str__이 return 될 수 있도록 함
3. __str__
간단한 인사와 이름을 문자열로 리턴하길 원하므로
def __str__(self):
return "안녕하세요 {}입니다.\n".format(self.name)
실습3 - 시계만들기
* 뒤 수업 듣기 전
필요 기능
- 현재 시간을 설정할 수 있다.
- 현재 시간을 변경할 수 있다.
- 현재 시간에 1초씩 더할 수 있다.
속성: 시간, 분, 초
행동: 시간 설정, 시간에 1초씩 더함
class Clock:
#시계 클래스
def __init__(self, hour, minute, sec):
self.hour = hour
self.minute = minute
self.sec = sec
def tick(self):
#1초씩 더하는 메소드
#60 sec = 1minute
#60 minute = 1 hour
#24 hour -> 0 시간으로 초기화
self.sec += 1
if self.sec == 60:
self.minute += 1
self.sec = 0
if self.minute == 60:
self.hour += 1
self.minute = 0
if self.hour == 24:
self.hour = 0
def set(self, hour, minute, sec):
self.hour = hour
self.minute = minute
self.sec = sec
def __str__(self):
return str(self.hour).zfill(2) + ":" + str(self.minute).zfill(2) + ":" + str(self.sec).zfill(2)
# 1시 30분 48초인 시계 인스턴스 생성
clock = Clock(1, 30, 48)
# 13초를 늘린다
for i in range(13):
clock.tick()
# 시계의 현재 시간 출력
print(clock)
# 2시 3분 58초로 시계 세팅
clock.set(2, 3, 58)
# 5초를 늘린다
for i in range(5):
clock.tick()
# 시계의 현재 시간 출력
print(clock)
# 23시 59분 57초로 세팅
clock.set(23, 59, 57)
# 5초를 늘린다
for i in range(5):
clock.tick()
# 시계의 현재 시간 출력
print(clock)
피드백:
tick 메소드가 복잡함
zfill을 이용해 표현할 때, 세번이나 중복됨
Counter 클래스를 도입하여 위 코드를 단순화 할 수 있다.
class Counter:
"""
시계 클래스의 시,분,초를 각각 나타내는데 사용될 카운터 클래스
"""
def __init__(self, limit):
"""
인스턴스 변수 limit(최댓값), value(현재까지 카운트한 값)을 설정한다.
인스턴스를 생성할 때 인스턴스 변수 limit만 파라미터로 받고, value는 초깃값 0으로 설정한다.
"""
self.limit = limit
self.value = 0
def set(self, new_value):
"""
파라미터가 0 이상, 최댓값 미만이면 value에 설정한다.
아닐 경우 value에 0을 설정한다.
"""
if 0 <= new_value < self.limit:
self.value = new_value
else:
self.value = 0
def tick(self):
"""
value를 1 증가시킨다.
카운터의 값 value가 limit에 도달하면 value를 0으로 바꾼 뒤 True를 리턴한다.
value가 limit보다 작은 경우 False를 리턴한다.
"""
self.value += 1
if self.value == self.limit:
self.value = 0
return True
return False
def __str__(self):
"""
value를 최소 두 자릿수 이상의 문자열로 리턴한다.
일단 str 함수로 숫자형 변수인 value를 문자열로 변환하고 zfill을 호출한다.
"""
return str(self.value).zfill(2)
class Clock:
"""
시계 클래스
"""
HOURS = 24 # 시 최댓값
MINUTES = 60 # 분 최댓값
SECONDS = 60 # 초 최댓값
def __init__(self, hour, minute, second):
"""
각각 시, 분, 초를 나타내는 카운터 인스턴스 3개(hour, minute, second)를 정의한다.
현재 시간을 파라미터 hour시, minute분, second초로 지정한다.
"""
self.hour = Counter(Clock.HOURS)
self.minute = Counter(Clock.MINUTES)
self.second = Counter(Clock.SECONDS)
self.set(hour, minute, second)
#self.hour.set(hour)
#self.minute.set(minute)
#self.second.set(second)
def set(self, hour, minute, second):
"""현재 시간을 파라미터 hour시, minute분, second초로 설정한다."""
self.hour.set(hour)
self.minute.set(minute)
self.second.set(second)
def tick(self):
"""
초 카운터의 값을 1만큼 증가시킨다.
초 카운터를 증가시킬 때, 분 또는 시가 바뀌어야하는 경우도 처리한다.
"""
if self.second.tick():
if self.minute.tick():
self.hour.tick()
def __str__(self):
"""
현재 시간을 시:분:초 형식으로 리턴한다. 시, 분, 초는 두 자리 형식이다.
예시: "03:11:02"
"""
return str(self.hour) + ":" + str(self.minute) + ":" + str(self.second)
# 초가 60이 넘을 때 분이 늘어나는지 확인하기
print("시간을 1시 30분 48초로 설정합니다")
clock = Clock(1, 30, 48)
print(clock)
# 13초를 늘린다
print("13초가 흘렀습니다")
for i in range(13):
clock.tick()
print(clock)
# 분이 60이 넘을 때 시간이 늘어나는지 확인
print("시간을 2시 59분 58초로 설정합니다")
clock.set(2, 59, 58)
print(clock)
# 5초를 늘린다
print("5초가 흘렀습니다")
for i in range(5):
clock.tick()
print(clock)
# 시간이 24가 넘을 때 00:00:00으로 넘어가는 지 확인
print("시간을 23시 59분 57초로 설정합니다")
clock.set(23, 59, 57)
print(clock)
# 5초를 늘린다
print("5초가 흘렀습니다")
for i in range(5):
clock.tick()
print(clock)
Counter 클래스
시계의 매커니즘은 다음과 같다.
1. 시간 설정이 가능
2. 시간 변경이 가능
3. 1초씩 더함 -> 60 sec = 1 min / 60 min = 1h / 24h -> 0h로 초기화
4. 시간을 표시할 때 2자리 숫자로 표현
Counter 클래스는 3번특성 때문에 혼자서 만들었을 때 복잡해진 tick에서 반복되는 부분을 한번에 모았다.
hour, minute, second는 각각 최대값이 존재하므로 최대값에 접근 하였을 때 초기화 되는 매커니즘!
그에 더해 기본적인 시계의 매커니즘을 구현.
class Counter:
def __init__(self, limit):
self.limit = limit
self.value = 0
#limit값은 다 다르므로 파라미터 값으로 받아줌
#value값은 0으로 설정
def set(self, new_value):
value = new_value if 0 <= new_value < limit else 0
#값을 사용자 마음대로 설정하는 메소드
#new_value 값이 0과 limit 사이일 때만 value값에 저장
def tick(self):
self.value += 1
if self.value == self.limit:
self.value = 0
#limit값에 도달하였으므로 0으로 값 초기화
return True
return False
#True, False 값을 이용하여 조건문에 활용이 가능하다.
def __str__(self):
return str(self.value).zfill(2)
#2자리 숫자로 표현하므로 zfill을 이용
Clock 클래스
본격적으로 시계의 본체가 될 클래스
counter 클래스를 적절히 이용하면 쉽게 만들 수 있다.
이번에도 역시 시계가 취해야할 행동을 생각하며 만들기
* 시계에서 시, 분, 초 각각의 limit값이 명확하므로 Clock 클래스 내부에 정의를 해놓자
class Clock:
HOURS = 24
MINUTES = 60
SECONDS = 60
#Clock 내부에서 사용될 각각의 limit값
1. __init__(self, hour, minute, second)
시계에 정의될 self.hour, self.minute, self.second값들은 limit값이 정해져있으므로 Counter 클래스를 이용한 인스턴스가 될 것이다. 인스턴스가 된 각각의 값에 파라미터 값 그대로를 대입하면 오류가 발생하므로 Counter 클래스의 set 메소드를 이용하여 각각의 값을 할당할 수 있다.
def __init__(self, hour, minute, second):
self.hour = Counter(Clock.HOURS) #Clock 내부에서 정의된 상수이므로
self.minute = Counter(Clock.MINUTES)
self.second = Counter(Clock.SECONDS)
self.hour.set(hour)
self.minute.set(minute)
self.second.set(second)
#인스턴스들이므로 set 메소드를 이용하여 값 할당
2. set(self, hour, minute, second)
처음 Clock 클래스에 할당된 값들을 새로 받은 값으로 치환하는 메소드
이 역시 인스턴스들의 set 메소드를 이용하여 할당할 수 있다.
def set(self, hour, minute, second):
self.hour.set(hour)
self.minute.set(minute)
self.second.set(second)
*Clock 클래스 내부의 set 메소드는 __init__ 메소드에서 처음 값을 할당할 때와 똑같은 방식이다.
그러므로 중복을 피하고자 set 메소드를 이용하여 간단하게 표현이 가능하다.
def __init__(self, hour, minute, second):
self.hour = Counter(Clock.HOURS)
self.minute = Counter(Clock.MINUTES)
self.second = Counter(Clock.SECONDS)
self.set(hour, minute, second)
#self.hour.set(hour)
#self.minute.set(minute)
#self.second.set(second)
※주의점
self.hour.set(hour) 는 Counter 클래스로 만들어진 인스턴스 hour의 set 메소드를 이용한 것이고
self.set(hour, minute, second)는 Clock 클래스로 만들어진 인스턴스의 set 메소드를 이용한 것이다.
그러므로 두 문장은 같은 set이라도 다른 set이다.
3. tick(self)
tick은 1초를 더해주는 메소드이므로 파라미터를 받을 필요가 없다.
tick에서 가장 중요한 조건은 60 sec = 1 min / 60 min = 1h / 24h -> 0h 이므로 각각의 hour, minute, second 인스턴스가 가지고 있는 tick 메소드를 활용하여 조건문을 만들 수 있다.
def tick(self):
if self.second.tick():
if self.minute.tick():
self.hour.tick()
#if는 조건 = True 일 때 발동
#초에서부터가 단위가 바뀌므로 self.second.tick()을 먼저 사용
#self.second.tick()을 부를 시 무조건 second의 값이 1 올라가고 False나 True값 반환
#-> second의 값이 1 올라가는 것이 먼저이므로 if구문이 발동되지 않더라도 second += 1 상태임
#True값 반환 = limit 도달이므로 self.minute.tick()으로 minute값을 1 올린다.
#self.minute.tick() 이하동문
4. __str__(self)
print문을 사용하면 자동으로 나오는 메소드.
def __str__(self):
return "{}:{}:{}".format(self.hour, self.minute, self.second)
#인스턴스 속 __str__ 에서 이미 str로 타입 변환과 zfill을 이용한 처리를 이미 끝냈으므로
#format을 이용하여 return만 하면 된다.
'program() > 파이썬' 카테고리의 다른 글
| (23.02.06) 객체 지향 프로그래밍 기본 개념 4. 다형성(Polymorphism) (0) | 2023.02.06 |
|---|---|
| (23.02.03) 객체 지향 프로그래밍 기본 개념 3. 상속(Inheritance) (0) | 2023.02.03 |
| (23.02.03) 객체 지향 프로그래밍 기본 개념 2. 캡슐화(Encapsulation) (0) | 2023.02.03 |
| (23.02.02) 객체 지향 프로그래밍 기본 개념 1. 추상화(Abstraction) (0) | 2023.02.02 |
| (23.01.08) 객체 타입, 객체 지향 / 절차 지향 (0) | 2023.01.18 |