x의 각 배열마다 다음에 올 값인 0.4, 0.5, 0.6, 0.7, 0.8, 0.9에 근접하는 값이 출력 되었고, 테스트 배열의 다음에 올 값인 1.0에 근접하는 값이 출력되었다. 학습 횟수를 10000번으로 설정했지만 이 횟수를 더 늘리면 목표 값에 더 근접한 값이 나올 것이다.
CNN은 Convolutional Neural Network의 약자로 주로 이미지 인식에 많이 사용된다. 이미지를 작은 특징에서 복잡한 특징으로 추상화하는 방식이다. 컬러 사진의 데이터는 3차원 형태로 들어오게 되는데, 사진 데이터를 픽셀값을 가진 2차원 행렬로 표현한다. 한 개의 픽셀은 RGB를 의미하는 3개의 색상값을 가지므로 1개의 픽셀은 3차원으로 구성된다.
CNN모델의 구조는 다음과 같다.
Convolution Layer에서 입력 데이터에 필터 적용후 활성화 함수를 반영하여 특징을 추출한다. Pooling Layer에서 Convolution Layer의 출력 데이터를 입력으로 받아서 크기를 줄이거나 특정 데이터를 강조하는 용도로 사용된다.
DNN은 Deap Neural Network의 약자이다. 은닉 계층이 여러개인 인공신경망을 의미한다.
은닉계층은 신경망의 외부에서 직접 접근할 수 없는 노드들을 의미한다.
제 1 은닉계층의 결과는 출력 계층이 아닌 제 2 은닉계층으로 들어가고, 제 2 은닉계층의 결과도 다음 은닉계층으로 계속 들어갈 수 있다. 이런식으로 다수의 은닉 계층을 활용하면 은닉계층 하나를 활용하는 것 보다 입력값을 더 정교하게 처리할 수 있다. 다음은 dnn을 이용하여 mnist 데이터를 학습시키는 코드이다.
처음 바둑판 상태를 입력받고, 뒤집기할 횟수를 입력받고, 좌표를 입력받는다. 반복문으로 반복하며 좌표의 가로줄과 세로줄의 흑, 백을 변환한다. 처음 바둑판 상태는 판 리스트를 하나 만들어 두고, 입력값 각 줄을 리스트로 만들어 기존에 만들어둔 리스트에 넣어 2차원 리스트로 만들었다. 십자 뒤집기는 입력받은 좌표의 세로줄과 가로줄 각각 반복문으로 19번 반복하며 흑과 백을 변환하였다.
pan = []
# 바둑판 초기 상태 입력
for i in range(19):
a = list(map(int, input().split()))
pan.append(a)
n = int(input())
for i in range(n):
# 십자 뒤집기 할 좌표
x, y = map(int, input().split())
# 리스트 인덱스는 0번부터 시작하므로 1씩 빼줌
x -= 1
y -= 1
# 가로 줄 흑, 백 변환
for j in range(19):
if pan[x][j] == 0:
pan[x][j] = 1
else:
pan[x][j] = 0
# 세로줄 흑, 백 변환
for j in range(19):
if pan[j][y] == 0:
pan[j][y] = 1
else:
pan[j][y] = 0
# 최종 값 출력
for i in range(19):
for j in range(19):
print(pan[i][j], end = ' ')
print()
1098 : [기초-2차원배열] 설탕 과자 뽑기
입력받은 가로, 세로 크기 만큼 0을 채운 격자판을 2차원 리스트로 만들고, 길이, 방향, 좌표를 입력받아 입력받은 좌표부터 가로 또는 세로 방향으로 입력받은 길이까지 1로 채우는 행위를 입력받은 좌표 수만큼 반복하고, 막대가 채워진 최종 격자판을 출력한다.
h, w = map(int, input().split())
pan = []
# 격자판 초기상태(모든 칸 0)
for i in range(h):
pan.append([0] * w)
n = int(input())
for i in range(n):
l, d, x, y = map(int, input().split())
x -= 1
y -= 1
# 막대가 가로로 놓여있을 때
if d == 0:
# 시작 좌표부터 가로 길이만큼 1로 채워짐
for j in range(l):
pan[x][y+j] = 1
# 세로로 놓여있을 때
else:
# 시작 좌표부터 세로 길이만큼 1로 채워짐
for j in range(l):
pan[x+j][y] = 1
# 격자판 채운 모양 출력
for i in range(h):
for j in range(w):
print(pan[i][j], end = ' ')
print()
1099 : [기초-2차원배열] 설탕 과자 뽑기
리스트의 인덱스는 0부터 시작하기 때문에 시작 인덱스를 1,1 로 지정하였고, 입력값은 한 줄씩 리스트로 만들어 입력받아 2차원 리스트로ㅓ 만들었다. 무한 루프 반복문을 만들어 개미가 먹이를 만나거나, 오른쪽 또는 아래로 이동할 수 없을 때 반복문을 탈출하도록 하였고, 벽이 없으면 오른쪽으로 벽이 있으면 아래로 이동하면서 현재 위치에 9를 저장하였다.
miro = []
x, y = 1, 1
for i in range(10):
a = list(map(int, input().split()))
miro.append(a)
while True:
if miro[x][y] == 2:
miro[x][y] = 9
break
miro[x][y] = 9
if miro[x][y+1] == 0:
y += 1
elif miro[x][y+1] == 1:
if miro[x+1][y] == 1:
break
elif miro[x+1][y] == 2:
miro[x+1][y] = 9
break
else:
x += 1
else:
miro[x][y+1] = 9
break
for i in range(10):
for j in range(10):
print(miro[i][j], end = ' ')
print()
for 반복문으로 1부터 입력받은 수까지 반복하고, 반복문 안에서 짝수일 경우 그 숫자들의 합을 누적하여 저장한다.
n = int(input())
s = 0
for i in range(1,n+1):
if i % 2 == 0:
s += i
print(s)
1079 : [기초-종합] 원하는 문자가 입력될 때 까지 반복 출력하기(설명)
입력값들을 변수 하나에 공백으로 분리하여 입력받고, 입력 받은 문자 수만큼 반복하여 각 문자를 각 줄에 출력하고, q를 확인하면 반복문을 종료한다.
chars = input().split()
for i in chars:
print(i)
if i == 'q':
break
1080 : [기초-종합] 언제까지 더해야 할까?
정수를 입력받아, 1부터 그 정수 만큼 반복하며 합을 누적한다. 누적된 합이 입력받은 정수보다 같거나 커지면 마지막으로 더한 정수를 출력하고 반복문을 종료시킨다.
n = int(input())
s = 0
for i in range(1, n):
s += i
if s >= n:
print(i)
break
1081 : [기초-종합] 주사위를 2개 던지면?(설명)
첫 번째 주사위 면이 1부터 n일 때 각 면의 주사위 숫자에 두번째 주사위 1부터 m이 조합될 수 있다. 1부터 n 까지 반복하면서 그 안에서 1부터 m까지 반복하여 첫번째 주사위 면이 1일때 두번째 주사위 면이 1, 2, 3, ... , m인 경우를 모두 출력할 수 있다.
n, m = map(int, input().split())
for i in range(1, n+1):
for j in range(1, m+1):
print(i,j)
1082 : [기초-종합] 16진수 구구단?
입력 받은 값을 1부터 F까지 반복하여 계산하여 출력하면 된다. 출력 예시를 보니 16진수 인데, 0x가 안붙어 있고, 다 대문자로 출력되어 있어서, 출력값을 문자열로 바꿔서 인덱스 슬라이싱과 .upper()을 통해 출력 형태를 예시와 동일하게 맞췄다.
dan = input()
dan = int(dan, 16)
for i in range(1, 16):
print('{}*{}={}'.format(str(hex(dan))[2:].upper(),
str(hex(i))[2:].upper(), str(hex(dan*i))[2:].upper()))
1083 : [기초-종합] 3 6 9 게임의 왕이 되자!(설명)
1부터 입력값까지 반복하는 코드를 작성하고, 반복문 안에서 현재 값이 3 또는 6또는 9면 or연산을 이용하여 X를 출력하고 continue를 이용하여 반복 처음을 돌아가고, 3, 6, 9가 아니라면 그 숫자를 출력한다. 출력은 한 줄로 한 칸 씩 띄워서 하므로 end = ' '를 사용하였다.
n = int(input())
for i in range(1,n+1):
if i == 3 or i == 6 or i == 9:
print('X', end = ' ')
continue
print(i, end=' ')
1084 : [기초-종합] 빛 섞어 색 만들기(설명)
r, g, b는 각각 하나라도 다른 값을 가지면 다른 색을 만들수 있으므로 0부터 각 색에 대한 입력값까지 3번 중첩하여 반복하면 모든 경우의 수를 출력할 수 있고, 각 경우의 조합을 출력할때 가지수를 세는 변수를 만들어 1씩 누적한후 마지막에 누적한 값을 출력하면 된다.
r, g, b = map(int, input().split())
cnt = 0
for i in range(0, r):
for j in range(0, g):
for k in range(0, b):
print(i,j,k)
cnt += 1
print(cnt)
1085 : [기초-종합] 소리 파일 저장용량 계산하기(설명)
입력받은 h,b,c,s를 위에서 제시한 계산식에 넣어 계산하고, MB단위로 맞추면 된다. 위 제시한 계산식에 넣어 계산했을 때의 단위는 bit이기 때문에 MB 단위로 표현하려면 8로 한번 나누고, 1024로 두번 나눈 후 소수점 첫째자리까지 출력하면 된다.
h, b, c, s = map(int, input().split())
pcm = h * b * c * s
mb = pcm / 8 / 1024 / 1024
print(format(mb, '.1f'), 'MB')
1086 : [기초-종합] 그림 파일 저장용량 계산하기(설명)
입력 값들을 문제에서 제시한 계산식에 넣어 계산 한 뒤 MB단위로 바꾸면 된다. 계산식에 넣어 계산하면 단위가 비트이기 때문에 8로 나눈 후 1024로 두번 나누면 된다.
w, h, b = map(int, input().split())
bit = w * h * b
mb = bit / 8 / 1024 /1024
print(format(mb,'.2f'), 'MB')
1087 : [기초-종합] 여기까지! 이제 그만~(설명)
1 부터 입력값 만큼 반복하며 각 숫자의 합을 누적한다. 누적한 값이 입력한 값과 같아지거나 더 커지면 break로 반복문을 종료하고 누적했던 값을 출력한다.
n = int(input())
s = 0
for i in range(1, n+1):
s += i
if s >= n:
break
print(s)
1088 : [기초-종합] 3의 배수는 통과?(설명)
for 반복문으로 1부터 입력 받은 숫자까지 출력하는데, 3의 배수이면 continue로 숫자를 출력하지 않고, 반복문 처음으로 돌아간다.
n = int(input())
for i in range(1, n+1):
if i % 3 == 0:
continue
print(i, end = ' ')
1089 : [기초-종합] 수 나열하기1
수학에서 등차수열 n번째 항을 구하는 공식을 이용하여 풀었다.
a, d, n = map(int, input().split())
an = a + (n-1)*d
print(an)
1090 : [기초-종합] 수 나열하기2
수학에서 등비수열의 n번째 항을 구하는 공식을 이용하여 풀었다.
a, r, n = map(int, input().split())
an = a * r ** (n-1)
print(an)
1091 : [기초-종합] 수 나열하기3
시작 값에 어떤 값을 곱하고, 더하는 행위를 계속 반복하므로 n번째 반복했을때의 수를 구하는 것이므로 반복문으로 n번째 까지 반복한 값을 출력했다.
a, m, d, n = map(int, input().split())
for i in range(n-1):
a = m * a + d
print(a)
1092 : [기초-종합] 함께 문제 푸는 날(설명)
가입한 다음 모두 같이 함께 방문하는 날은 각 방문 주기의 최소 공배수 이다. for문으로 하루씩 증가시키면서 and연산으로 방문 주기의 최소공배수를 출력하면 된다.
a, b, c = map(int,input().split())
day = 1
while True:
if day % a == 0 and day % b == 0 and day % c == 0:
print(day)
break
day += 1
1093 : [기초-1차원 배열] 이상한 출석번호 부르기1(설명)
무작위로 부른 출석 번호를 입력 받아 리스트로 만들고, 23개의 요소가 0으로 채워진 리스트를 만들어 23번 반복하며 리스트의 각 요소를 무작위로 부른 리스트에서 각 번호가 몇번 불러졌는지 .count로 세서 넣었다.
n = int(input())
num = map(int, input().split())
num = list(num)
num_li = [0]*23
for i in range(len(num_li)):
num_li[i] = num.count(i+1)
for i in num_li:
print(i, end = ' ')
1094 : [기초-1차원 배열] 이상한 출석번호 부르기2(설명)
리스트는 list.reverse()와 같은 형식으로 거꾸로 뒤집을 수 있다. 무작위로 부른 번호를 리스트로 만들고 거꾸로 뒤집은 후 출력하면 된다.
n = int(input())
num = map(int, input().split())
num_li = list(num)
num_li.reverse()
for i in num_li:
print(i, end=' ')
1095 : [기초-1차원 배열] 이상한 출석번호 부르기3(설명)
list.sort()와 같이 사용하면 리스트가 오름차순으로 정렬된다. 무작위로 부른 번호들을 리스트로 만들어 오름차순으로 정렬한 후 첫번째 요소를 출력하면 된다.
n = int(input())
num = map(int, input().split())
num_li = list(num)
num_li.sort()
print(num_li[0])
1096 : [기초-2차원 배열] 바둑판에 흰 돌 놓기(설명)
19 * 19 크기의 2차원 리스트의 모든 요소를 다 0으로 초기화 하고, 흰돌의 개수를 입력 받고, 개수 만큼 반복하여 해당 위치의 요소를 1로 바꿨다. for 문 2중 중첩으로 2차원 리스트의 모든 요소를 바둑판 모양으로 출력하였다.
n = int(input())
pan = [[0]*19 for i in range(19)]
for i in range(n):
x, y = map(int, input().split())
pan[x-1][y-1] = 1
for i in range(19):
for j in range(19):
print(pan[i][j], end = ' ')
print()
파이썬에서는 16진수는 0x가 붙기 때문에 입력값에 0x를 붙여줘야 한다. 입력값에 0x를 붙이고 int(변수, 현재진법)의 형식으로 10진수로 변환한 다음 다시 oct()로 8진수로 변환한다. 변환한 값을 다시 문자열로 변환한 후 인덱스 슬라이싱을 이용하여 0o를 빼고 출력한다.
n = input()
n = '0x' + n
n = int(n, 16)
n = oct(n)
n = str(n)
print(n[2:])
1036 : [기초-출력변환] 영문자 1개 입력받아 10진수로 출력하기(설명)
문자를 아스키 코드표의 10진수로 변환하려면 파이썬에서는 ord()함수를 이용한다. 입력받은 값을 ord로 10진수로 변환한 후 출력한다.
a = input()
n = ord(a)
print(n)
1037 : [기초-출력변환] 정수 입력받아 아스키 문자로 출력하기(설명)
10진수를 아스키 문자로 변환하려면 파이썬에서는 chr()함수를 사용한다. 입력 받은 값을 chr로 변환한 후 출력한다.
n = int(input())
a = chr(n)
print(a)
1038 : [기초-산술연산] 정수 2개 입력받아 합 출력하기1(설명)
map으로 두 입력값을 정수로 입력받고, 입력받은 값들을 더하여 출력한다.
n1, n2 = map(int, input().split())
print(n1+n2)
1039 : [기초-산술연산] 정수 2개 입력받아 합 출력하기2(설명)
파이썬에서는 정수 범위의 제한이 없기 때문에 위 코드를 동일하게 사용하면 된다.
n1, n2 = map(int, input().split())
print(n1+n2)
1040 : [기초-산술연산] 정수 1개 입력받아 부호 바꿔 출력하기(설명)
정수를 저장한 변수에 -(마이너스)기호를 붙이면 부호가 바뀐다.
n = int(input())
print(-n)
1041 : [기초-산술연산] 문자 1개 입력받아 다음 문자 출력하기(설명)
아스키 문자는 알파벳 순서대로 배열되어 있어 아스키 코드에 1을 더하면 다음 알파벳이 나온다. 입력받은 문자를 아스키 코드로 변환한 후 1을 더한 후에 다시 문자로 변환하여 출력한다.
a = input()
a = chr(ord(a) +1)
print(a)
1042 : [기초-산술연산] 정수 2개 입력받아 나눈 몫 출력하기(설명)
파이썬에서는 //연산자로 몫연산을 진행한다. 나머지는 버리고 몫만 반환한다.
a, b = map(int, input().split())
print(a//b)
1043 : [기초-산술연산] 정수 2개 입력받아 나눈 나머지 출력하기(설명)
나머지를 구하는 연산자는 %이다. 입력값을 정수로 입력받아 나머지 연산하여 출력한다.
a, b = map(int, input().split())
print(a%b)
1044 : [기초-산술연산] 정수 1개 입력받아 1 더해 출력하기(설명)
입력받은 값에서 1 더한 값을 출력하면된다.
n = int(input())
print(n+1)
1045 : [기초-산술연산] 정수 2개 입력받아 자동 계산하기(설명)
두 입력값을 정수로 입력받아 각 줄에 각각 연산한 값을 출력한다. 나눈 값은 실수로 출력되기 때문에 format으로 소숫점 둘째자리 까지 출력할 수 있도록 고정해준다.
*2 로 출력해도 되지만, 비트 시프트를 이용할 수 있다. 2진수 형태의 값을 <<(왼쪽)으로 밀면 값이 두 배 증가하고, >>(오른쪽)으로 밀면 값이 반으로 줄어든다. 입력받은 값을 비트시프트를 이용하여 1칸 밀면 2배 곱한 값을 출력할 수 있다.
n = int(input())
print(n<<1)
1048 : [기초-비트시프트연산] 한 번에 2의 거듭제곱 배로 출력하기(설명)
왼쪽(<<)으로 밀 때 마다 값이 두번씩 증가하기 때문에 a를 b칸만큼 밀면 된다.
a, b = map(int, input().split())
print(a<<b)
1049 : [기초-비교연산] 두 정수 입력받아 비교하기1(설명)
비교 연산자는 왼쪽이 오른쪽보다 크면 > , 작으면 <, 크거나 같으면 >=, 작거나 같으면 <=, 같으면 ==, 다르면 != 을 사용한다. if 조건문을 사용하여 a > b일 경우 1을 출력하고 아니면 0을 출력하면 된다.
a, b = map(int, input().split())
if a > b:
print(1)
else:
print(0)
1050 : [기초-비교연산] 두 정수 입력받아 비교하기2(설명)
두 정수가 같으면 == 연산자를 이용하여 비교했을 때 참이 나오고, 다르면 != 연산자를 이용하여 비교했을 때 참이 나온다. 이를 활용하여 if 조건문으로 해결할 수 있다.
a, b = map(int, input().split())
if a == b:
print(1)
else:
print(0)
1051 : [기초-비교연산] 두 정수 입력받아 비교하기3(설명)
크거나 같은지 확인하는 연산자는 >= 이다. if 조건문으로 크거나 같은지 확인하고, 아니라면 else에서 처리하면 된다.
a, b = map(int, input().split())
if b >= a:
print(1)
else:
print(0)
1052 : [기초-비교연산] 두 정수 입력받아 비교하기4(설명)
두 정수가 다른지 확인하는 연산자는 != 이다. if 조건문으로 다른지 확인하고 아니면 else에서 처리하면 된다.
a, b = map(int, input().split())
if b != a:
print(1)
else:
print(0)
1053 : [기초-논리연산] 참 거짓 바꾸기 (설명)
입력값은 둘 중 하나이기 때문에 입력값을 받아서 1이 입력되면 0을 출력하고, 아니면 1을 출력하도록 하면 된다.
a = int(input())
if a == 0:
print(1)
else:
print(0)
1054 : [기초-논리연산] 둘다 참일 경우만 참 출력하기 (설명)
if 조건문에 조건식을 넣을 때 and 연산자를 사용하면 조건식을 여러 개 넣을 수 있고, and연산자는 and의 왼쪽 식과 오른쪽 식이 모두 참 이여야 참을 반환한다. if 1 and 1: 과 같이 둘 다 참이여야 조건식을 수행한다. 따라서 둘 다 1을 입력 받을 경우 1을 출력하도록 하면 된다.
a, b = map(int,input().split())
if a == 1 and b == 1:
print(1)
else:
print(0)
1055 : [기초-논리연산] 하나라도 참이면 참 출력하기 (설명)
or 연산자는 둘 중 하나만 참이면 참을 출력한다. 위 코드에서 and 부분만 or로 바꾸면 된다.
a, b = map(int,input().split())
if a == 1 or b == 1:
print(1)
else:
print(0)
1056 : [기초-논리연산] 참/거짓이 서로 다를 때에만 참 출력하기 (설명)
입력 될 수 있는 값은 0 과 1 밖에 없으므로 둘이 다르면 1을 출력하고, 같으면 0을 출력한다.
a, b = map(int,input().split())
if a != b:
print(1)
else:
print(0)
1057 : [기초-논리연산] 참/거짓이 서로 같을 때에만 참 출력하기 (설명)
두 입력값이 서로 같으면 1을 출력하고, 아니면 0을 출력한다.
a, b = map(int,input().split())
if a == b:
print(1)
else:
print(0)
1058 : [기초-논리연산] 둘 다 거짓일 경우만 참 출력하기 (설명)
and 연산을 이용하여 입력받은 두 값이 모두 0일때 1을 출력한다.
a, b = map(int,input().split())
if a == 0 and b == 0:
print(1)
else:
print(0)
1059 : [기초-비트단위논리연산] 비트단위로 NOT 하여 출력하기 (설명)
10진수를 2진수로 바꾸면 0과 1의 배열로 표현한다. 이 배열에서 0은 1로, 1은 0으로 바꾸는것을 비트 단위로 NOT한다고 한다. 파이썬에서도 ~연산자를 통해 비트단위 NOT연산을 할 수 있다.
n = int(input())
print(~n)
1060 : [기초-비트단위논리연산] 비트단위로 AND 하여 출력하기 (설명)
비트 단위로 AND연산 하는 것은 두 수를 2진수 배열로 바꿨을때 각 자리가 둘다 1인 부분만 1로 하고 나머지는 0으로 만드는 것이다. &연산자를 통해 비트단위 AND연산을 할 수 있다.
a, b = map(int,input().split())
print(a & b)
1061 : [기초-비트단위논리연산] 비트단위로 OR 하여 출력하기 (설명)
비트 단위로 or 연산 하는 것은 두 수를 2진 배열로 바꿨을 때 각 자리중 하나만 1이면 1로하고, 둘다 0이면 0으로 만드는 것이다. | 연산자를 통해 비트단위 OR연산을 할 수 있다.
a, b = map(int,input().split())
print(a | b)
1062 : [기초-비트단위논리연산] 비트단위로 XOR 하여 출력하기 (설명)
비트 단위로 XOR연산 하는 것은 두 수를 2진 배열로 바꿨을 때 각 자리의 수가 서로 다르면 1로 하고, 같으면 0으로 하는 것이다. ^연산자를 이용하여 비트단위 XOR연산을 할 수 있다.
a, b = map(int,input().split())
print(a ^ b)
1063 : [기초-삼항연산] 두 정수 입력받아 큰 수 출력하기 (설명)
파이썬에서는 삼항연산을 다음과 같이 수행한다.
참일때 반환값 if 조건 else 거짓일때 반환값
따라서 정답은 다음과 같다.
a,b = map(int, input().split())
print(a if a > b else b)
1064 : [기초-삼항연산] 정수 3개 입력받아 가장 작은 수 출력하기 (설명)
a와 b중 작은 값을 삼항 연산으로 구하는 식을 괄호에 넣어서 괄호를 c와 비교하는 방식으로 삼항 연산을 구성할 수 있다.
a,b,c = map(int, input().split())
print((a if a < b else b) if (a if a < b else b) < c else c)
1065 : [기초-조건/선택실행구조] 정수 3개 입력받아 짝수만 출력하기 (설명)
if 조건문을 활용해서 입력받은 각 수가 짝수일 때만 출력하도록 한다. 짝수는 2로 나눴을 때 나머지가 0이면 짝수이다.
a,b,c = map(int, input().split())
if a % 2 == 0:
print(a)
if b % 2 == 0:
print(b)
if c % 2 == 0:
print(c)
1066 : [기초-조건/선택실행구조] 정수 3개 입력받아 짝/홀 출력하기 (설명)
각 변수마다 if~else문으로 짝이면 even을 출력하고, 아니면 odd를 출력한다.
a,b,c = map(int, input().split())
if a % 2 == 0:
print('even')
else:
print('odd')
if b % 2 == 0:
print('even')
else:
print('odd')
if c % 2 == 0:
print('even')
else:
print('odd')
1067 : [기초-조건/선택실행구조] 정수 1개 입력받아 분석하기 (설명)
if 조건문으로 먼저 입력받은 정수가 양수인지 음수인지 확인하여 출력하고, 그 안에서 다시 if문으로 짝수인지 홀수인지 확인하여 출력한다.
a = int(input())
if a > 0 :
print('plus')
if a % 2 == 0:
print('even')
else:
print('odd')
else:
print('minus')
if a % 2 == 0:
print('even')
else:
print('odd')
1068 : [기초-조건/선택실행구조] 정수 1개 입력받아 평가 출력하기(설명)
if 문으로 입력받은 점수를 비교하여 평가를 출력해야 한다. 문제와 같이 검사해야 할 조건이 많을 때는 if 다음에 elif를 사용하면 if 조건이 거짓일 경우 순차적으로 아래로 내려오며 elif의 조건식을 검사한다.
month = int(input())
if month == 12 or month == 1 or month == 2:
print('winter')
elif month == 3 or month == 4 or month == 5:
print('spring')
elif month == 6 or month == 7 or month == 8:
print('summer')
else:
print('fall')
1071 : [기초-반복실행구조] 0입력될 때까지 무한 출력하기1(설명)
파이썬에서는 goto문이 없다. 입력 예시를 보니 정수가 한번에 입력되기 때문에 한 변수 x에 입력 받아서 for 반복문으로 반복하며 x의 각 요소들이 0인지 비교하고, 0이 아니면 그 요소를 출력하고, 0이면 반복문을 종료한다.
x = map(int, input().split())
for i in x:
if i == 0:
break
else:
print(i)
1072 : [기초-반복실행구조] 정수 입력받아 계속 출력하기(설명)
입력 받을 정수의 개수를 입력받고, 정수들을 하나의 변수에 입력받는다. 정수들을 입력받은 변수를 for 반복문으로 반복하며 각 요소를 출력한다.
n = int(input())
num = map(int, input().split())
for i in num:
print(i)
1073 : [기초-반복실행구조] 0입력될 때까지 무한 출력하기2(설명)
1071번 문제와 동일하다. 1071번 문제는 goto문을 사용하는것이 문제의 의도인듯 했으나 파이썬에서는 goto는 사용할 수 없어 for 문으로 풀었다. 이 문제도 1071번과 동일한 코드를 사용하겠다.
x = map(int, input().split())
for i in x:
if i == 0:
break
else:
print(i)
1074 : [기초-반복실행구조] 정수 1 개 입력받아 카운트다운 출력하기1(설명)
while문을 이용하여 해결할 수 있다. while 조건식에 입력받은 정수가 0이 될 때 까지 반복 하도록 하고, 입력받은 정수를 출력하고 거기서 1을 빼는 것을 반복한다.
n = int(input())
while n != 0:
print(n)
n -= 1
1075 : [기초-반복실행구조] 정수 1 개 입력받아 카운트다운 출력하기2(설명)
이번에는 for 반복문을 이용하여 range(입력값, 끝날값, 증가시킬 크기)와 같은 형식으로 반복 실행할 수 있는데 1씩 줄여야 하므로 증가시킬 크기에 -1을 넣으면 된다. 그리고 끝날 값은 실제 출력값보다 1 작게 넣어줘야 한다. 첫번째 출력값도 입력값보다 1 작으므로 입력값 - 1 을 넣어줘야 한다.
n = int(input())
for i in range(n-1, -1, -1):
print(i)
1076 : [기초-반복실행구조] 문자 1 개 입력받아 알파벳 출력하기(설명)
영소문자는 97 부터 시작한다. 입력한 값을 아스키 코드 10진값으로 바꾸고 97 부터 입력값을 바꾼 아스키 코드 값까지 반복하여 chr()로 문자로 출력하면 된다. 공백을 두고 1열로 출력해야하므로 print에 sep = ' '을 설정해줘야 한다.
a = input()
a = ord(a)
for i in range(97, a+1):
print(chr(i), sep = ' ')
1077 : [기초-반복실행구조] 정수 1 개 입력받아 그 수까지 출력하기(설명)
for문으로 0부터 입력받은 수까지 출력할 수 있다. range로 범위 설정을 할때는 출력하려고 하는 수보다 1 크게 설정해야 한다.
입력받은 값을 실수형으로 형변환 하고 출력하면 된다. 답을 제출하니 오답이라 나와서 테스트 케이스들을 확인하니 소숫점이 6자리로 고정인것 같다. 실수를 출력할때 format(실수, ".자리수f")와 같은 형식으로 출력하면 원하는 소숫점 자리수 만큼 출력할 수 있다. 정답은 다음 코드와 같다.
f = float(input())
print(format(f, ".6f"))
1013 : [기초-입출력] 정수 2개 입력받아 그대로 출력하기(설명)
map을 활용하여 한번에 두 개의 정수를 입력받아 정수로 형변환 할 수 있다. split()은 공백을 기준으로 입력값을 구분한다.
n1, n2 = map(int, input().split())
print(n1,n2)
1014 : [기초-입출력] 문자 2개 입력받아 순서 바꿔 출력하기(설명)
문자 두개를 입력 받고, 출력할 때 변수의 순서를 지정하면 된다.
a, b = input().split()
print(b,a)
1015 : [기초-입출력] 실수를 입력받아 둘째 자리까지 출력하기(설명)
format(실수, ".자리수f")와 같은 형식으로 출력하면 지정한 자리수 만큼 반올림되어 출력된다.
f = float(input)
print(format(f, ".2f")
1017 : [기초-입출력] 정수 1개 입력받아 3번 출력하기(설명)
입력받은 정수를 저장한 변수를 3번 출력하면 된다.
1018 : [기초-입출력] 시간 입력받아 그대로 출력하기(설명)
split()괄호 안에 ':'을 넣으면 :을 기준으로 구분된다. print()에 .format()을 사용하면 문자열의 중괄호 안에 format의 인자로 넣은 변수가 순서대로 들어간다.
h, m = map(int, input().split(':'))
print('{}:{}'.format(h,m))
1019 : [기초-입출력] 연월일 입력받아 그대로 출력하기(설명)
split('.')로 입력값을 .을 기준으로 구분한다. 한 자리 수면 십의 자리에 0을 채워야 하는데, 중괄호에 format으로 값을 대입할 때 {:02d}와 같은 방식으로 자리수를 맞추면 된다. 두자리수면 그대로 출력되고, 한 자리 수면 2자리로 맞춰준다. 처음에 실패해서 테스트 케이스를 살펴보니 년도 4자리로 맞춰줘야 하는 것 같다.
y, m, d = map(int, input().split('.'))
print('{:04d}.{:02d}.{:02d}'.format(y,m,d))
1020 : [기초-입출력] 주민번호 입력받아 형태 바꿔 출력하기(설명)
입력값을 '-'을 기준으로 구분하여 각 변수에 저장하고, 출력할 때 붙여서 출력하면 된다. print함수의 sep는 두 개 이상의 변수를 출력할 때 구분 문자를 지정하는데, 기본값은 공백이였지만, sep=''으로 공백을 없애면 된다.
j1, j2 = input().split('-')
print(j1,j2,sep='')
1021 : [기초-입출력] 단어 1개 입력받아 그대로 출력하기(설명)
변수 하나에 입력값을 저장하고 print로 저장한 변수를 출력하면 된다.
s = input()
print(s)
1022 : [기초-입출력] 문장 1개 입력받아 그대로 출력하기(설명)
위 문제와 같이 변수 하나에 입력받아 그대로 출력하면 된다.
s = input()
print(s)
1023 : [기초-입출력] 실수 1개 입력받아 부분별로 출력하기(설명)
입력값을 '.'으로 구분하여 각 변수에 저장하여 print로 각 줄에 출력하면 된다.
n, f = input().split('.')
print(n)
print(f)
1024 : [기초-입출력] 단어 1개 입력받아 나누어 출력하기(설명)
입력받은 단어를 변수에 저장하고, for 반복문을 이용하여 한줄에 한 문자씩 출력할 수 있다. 테스트 케이스를 확인해보니 출력한 문자를 작은 따옴표로 감싸고 있었기에 format을 이용하여 작은따옴표 안에 넣어줬다
word = input()
for i in word:
print("'{}'".format(i))
위와 같은 방식으로 반복문을 사용하면 word변수에 들어있는 각 문자가 반복을 하며 순서대로 i에 들어간다.
1025: [기초-입출력] 정수 1개 입력받아 나누어 출력하기(설명)
숫자를 입력받고, 반복문으로 한자리씩 숫자의 자리에 맞게 곱하여 [] 안에 넣어 출력하였다. cnt는 자리수에 맞게 곱할 숫자인데, 입력받은 숫자가 5자리 이기 때문에 10000에서 시작한다. 반복 횟수를 거듭할수록 10씩 나눠주고 정수를 나누면 실수형이 되기 때문에 출력할때 다시 한번 정수형으로 형변환을 해줬다.
n = input()
cnt = 10000
for i in n:
pr_num = int(i) * cnt
print('[{}]'.format(int(pr_num)))
cnt /= 10
1026: [기초-입출력] 시분초 입력받아 분만 출력하기(설명)
split()으로 시, 분, 초를 구분하여 입력받고, 분을 저장한 변수만 출력한다.
h, m, s = map(int, input().split(':'))
print(m)
1027: [기초-입출력] 년월일 입력받아 형식 바꿔 출력하기(설명)
입력값은 split()으로 구분 받아서 각 변수에 저장한다. 저장한 변수를 .format()으로 순서를 바꾸고 {:02d}와 같은 형식으로 자리수를 바꾸어 출력하면 된다.
y, m, d = map(int, input().split('.'))
print('{:02d}-{:02d}-{:04d}'.format(d,m,y))
1028: [기초-데이터형] 정수 1개 입력받아 그대로 출력하기2(설명)
파이썬에서는 int자료형의 범위가 더 넓기 때문에 입력 받은 값을 int로 저장하여 출력하면 된다.
n = int(input())
print(n)
1029: [기초-데이터형] 실수 1개 입력받아 그대로 출력하기2(설명)
float로 실수를 입력받아 format(실수, ".11f")로 11자리까지 출력하면 된다.
f = float(input())
print(format(f, ".11f"))
1030: [기초-데이터형] 정수 1개 입력받아 그대로 출력하기3(설명)
파이썬에서는 int범위를 벗어나도 type를 확인해보면 int로 나오기 때문에 입력값을 int로 저장하여 그대로 출력하면 된다.
n = int(input())
print(n)
1031: [기초-출력변환] 10진정수 1개 입력받아 8진수로 출력하기(설명)
파이썬에서는 8진수를 출력하면 0o가 앞에 붙는데, 출력 예시를 보면 0o가 없다. 정수로 입력받은 입력값을 oct()로 8진수로 형변환 한 후 str로 다시 형변환을 하여 출력할 때 [2:]인덱스를 활용하여 0o를 제외한 2번 인덱스 부터 출력하였다.
n = int(input())
n = str(oct(n))
print(n[2:])
1032: [기초-출력변환] 10진정수 1개 입력받아 16진수로 출력하기1(설명)
16진수를 출력할 때도 0x가 앞에 붙는데, 출력 예시를 보면 0x가 없기 때문에 hex()로 16진수로 형변환 한 후, str로 형변환 하여 2번 인덱스부터 출력하면 된다.
n = int(input())
n = str(hex(n))
print(n[2:])
1033: [기초-출력변환] 10진정수 1개 입력받아 16진수로 출력하기2(설명)
대문자로 출력해야 하므로 위와 같이 출력하고, 출력 자료형이 str인 상태이기 때문에 .upper()를 사용하여 대문자로 출력할 수 있다.
n = int(input())
n = str(hex(n))
print(n[2:].upper())
1034: [기초-출력변환] 8진정수 1개 입력받아 10진수로 출력하기(설명)
파이썬에서는 0o가 앞에 붙어있어야 8진수로 인식하기 때문에 인식 받은 값에 0o를 붙여줘야 한다. int로 형변환 할때 int(변수, 현재진수)의 형식으로 형변환을 하면 변수의 값을 지정한 진법에서 int형으로 형변환 된다. 형변환 한 값을 출력하면 된다.
속성에는 클래스 속성과 인스턴스 속성 두 가지 종류가 있다. __init__메서드에서 만들었던 속성은 인스턴스 속성이다.
35.1.1 클래스 속성 사용하기
클래스 속성은 다음과 같이 클래스에 바로 속성을 만든다.
class 클래스이름:
속성 = 값
다음은 사람 클래스에 클래스 속성으로 가방 속성을 만들고, 가방에 물건을 넣는 put_bag메서드를 사용하는 코드이다.
class Person:
bag = []
def put_bag(self, stuff):
self.bag.append(stuff)
james = Person()
james.put_bag('book')
maria = Person()
maria.put_bag('key')
print(james.bag)
print(maria.bag)
결과를 보면 두 인스턴스에 넣었던 물건이 합쳐저서 출력되는것을 확인할 수 있다.
클래스 속성은 클래스에 속해있으면서 모든 인스턴스에서 공유된다.
클래스 속성에 접근할 때는 다음과 같이 클래스 이름으로 접근하면 코드가 좀 더 명확해진다.
class Person:
bag = []
def put_bag(self, stuff):
Person.bag.append(stuff)
클래스 밖에서도 다음과 같이 클래스 이름으로 클래스 속성에 접근하면 된다.
print(Person.bag)
속성, 메서드 이름을 찾는 순서
파이썬에서는 속성, 메서드 이름을 찾을 때 인스턴스, 클래스 순서로 찾는다. 그래서 인스턴스 속성이 있는지 먼저 확인하고, 클래스 속성을 찾는다. 인스턴스와 클래스에서 __dict__ 속성을 출력해보면 현재 인스턴스와 클래스의 속성을 딕셔너리로 확인할 수 있다.
print(james.__dict__)
print(Person.__dict__)
35.1.2 인스턴스 속성 사용하기
bag속성을 여러 객체가 공유하지 않으려면 bag를 인스턴스 속성으로 만들면 된다.
class Person:
def __init__(self):
self.bag = []
def put_bag(self, stuff):
self.bag.append(stuff)
james = Person()
james.put_bag('book')
maria = Person()
maria.put_bag('key')
print(james.bag)
print(maria.bag)
이제 james.bag과 maria.bag을 출력해보면 각자 넣은 물건만 출력된다.
인스턴스 속성은 인스턴스 별로 독립되어 있으며 서로 영향을 주지 않는다.
35.1.3 비공개 클래스 속성 사용하기
클래스 속성도 비공개 속성을 만들수 있다. 클래스 속성을 만들 때 __속성처럼 앞에 밑줄 두 개로 시작하면 비공개 속성이된다. 비공개 클래스 속성은 클래스 안에서만 접근할 수 있고, 밖에서는 접근할 수 없다.
class 클래스이름:
__속성 = 값
클래스에서 공개하고 싶지 않은 속성이 있다면 비공개 클래스를 사용해야 한다. 다음은 기사 게임 캐릭터의 아이템 최대 개수를 비공개 클래스 속성으로 작성한 것이다.
class Knight:
__item_limit = 10
def print_limit(self):
print(Knight.__item_limit)
x = Knight()
x.print_limit()
print(x.__item_limit)
위와 같이 비공개 클래스 속성은 클래스 밖에서는 접근할 수 없고, 클래스 내부의 메서드에서만 접근이 가능하다.
클래스와 메서드의 독스트링 사용하기
함수와 마찬가지로 클래스와 메서드 에서도 클론 바로 다음 줄에 """ """ 또는 ''' '''로 독스트링을 사용할 수 있고, 클래스.__doc__, 메서드.__doc__와 같이 출력할 수 있다.
인스턴스를 통하지 않고, 클래스에서 바로 호출할 수 있는 정적 메서드와 클래스 메서드가 있다.
정적 메서드는 다음과 같이 메서드 위에 @staticmethod를 붙인다. 이때 정적 메서드는 매개변수에 self를 지정하지 않는다.
class 클래스이름:
@staticmethod
def 메서드(매개변수1, 매개변수2)
코드
@staticmethod 처럼 앞에 @가 붙은 것을 데코레이터라고 하며 메서드(함수)에 추가 기능을 구현할 때 사용한다.
다음은 덧셈과 곱셈을 하는 클래스이다.
class Calc:
@staticmethod
def add(a,b):
print(a + b)
@staticmethod
def mul(a,b):
print(a*b)
Calc.add(5,3)
Calc.mul(5,3)
정적 메서드는 위와 같이 호출할 때 클래스에서 바로 메서드를 호출할 수 있다.
정적 메서드는 self를 받지 않기 때문에 인스턴스 속성에는 접근할 수 없다. 그래서 보통 정적 메서드는 인스턴스 속성, 인스턴스 메서드가 필요 없을 때 사용한다.
정적 메서드는 메서드의 실행이 외부 상태에 영향을 끼치지 않는 순수 함수를 만들 때 사용한다. 순수 함수는 부수 효과가 없고, 입력값이 같으면 언제나 같은 값을 반환한다. 정적 메서드는 인스턴스의 상태를 변화시키지 않는 메서드를 만들 때 사용한다.
파이썬의 자료형도 인스턴스 메서드와 정적 메서드로 나누어져 있다. 인스턴스의 내용을 변경시켜야 할 땐 인스턴스 메서드를 사용하고, 인스턴스 내용과 상관 없이 결과만 구하면 될 때는 정적 메서드를 이용한다.
35.3 클래스 메서드 사용하기
클래스 메서드는 메서드 위에 @classmethod를 붙인다. 첫번째 매개변수로는 cls를 지정해야 한다.(cls는 class에서 따온 것이다.)
class 클래스이름:
@classmethod
def 메서드(cls, 매개변수1, 매개변수2)
코드
다음은 사람 클래스에 인스턴스가 몇 개 만들어졌는지 출력하는 클래스 메서드를 만들었다.
class Person:
count = 0
def __init__(self):
Person.count += 1
@classmethod
def print_count(cls):
print('{} people'.format(cls.count))
james = Person()
maria = Person()
Person.print_count()
인스턴스가 만들어 질때마다 __init__메서드로 클래스 속성 count에 1을 더해준다.
클래스 메서드의 매개변수 cls는 현재 클래스가 들어오기 때문에 cls.count와 같은 방식으로 cls로 클래스 속성 count 에 접근할 수 있다.
print_count 메서드는 클래스 메서드이기 때문에 Person.print_count처럼 클래스로 호출해야 한다.
Person으로 인스턴스를 두 개 만들었기 때문에 '2 people'이 출력된다.
클래스 메서드는 메서드 안에서 클래스 속성, 클래스 메서드에 접근해야 할 때 사용한다.
다음과 같이 cls를 사용하여 현재 클래스의 인스턴스를 만들 수도 있다. cls()는 Person()와 같다.
@classmethod
def create(cls):
p = cls()
return p
35.4 퀴즈
정답은 a 이다.
정답은 c이다.
정답은 c,e이다. 클래스 메서드의 첫번째 매개변수는 cls이다. 클래스 메서드는 위에 @classmethod를 붙여야 한다.
35.5 연습문제: 날짜 클래스 만들기
정답은 다음 코드와 같다.
@staticmethod
def is_date_valid(date):
year, month, day = map(int, date.split('-'))
return 0 < month <= 12 and 0 < day <= 31
클래스.메서드의 형식으로 호출했기 때문에 정적 메서드를 사용하여 날짜 형식에 따라 True와 False를 반환하도록 하였다.
35.6 심사문제: 시간 클래스 만들기
정답은 다음 코드와 같다.
@staticmethod
def is_time_valid(time):
h, m, s = map(int, time.split(':'))
return 0 < h <=24 and 0 <= m <= 59 and 0 <= s <= 60
@classmethod
def from_string(cls, time):
h, m, s = map(int, time.split(':'))
t = cls(h,m,s)
return t
is_time_valid는 조건문에 들어가 True와 False만 반환하면 되기에 정적 메서드로 입력값을 시, 분, 초로 분리하여 각 값이 올바른 형식인지 검사하였다. from_string는 인스턴스 속성이 필요하기 때문에 클래스 메서드로 메서드 내에서 입력값을 시, 분, 초로 분리하여 새로운 인스턴스를 만들어 반환하였다.
Unit 36. 클래스 상속 사용하기
클래스 상속은 물려받은 기능을 그대로 유지한 채로 다른 기능을 추가할 때 사용하는 기능이다. 기능을 물려주는 클래스를 기반 클래스, 상속을 받아 새롭게 만드는 클래스를 파생 클래스라고 한다. 보통 기반클래스를 부모클래스 또는 슈퍼클래스 라고 부르고, 파생클래스를 자식클래스 또는 서브클래스 라고 부른다.
새로운 기능이 필요할 때 마다 계속 클래스를 만들면 중복되는 부분도 계속해서 만들어야 한다. 이때 상속을 사용하면 중복되는 기능을 만들지 않아도 되기 때문에 상속클래스는 기존 기능을 재사용 할 수 있어서 효율적이다.
36.1 사람 클래스로 학생 클래스 만들기
클래스 상속은 다음과 같이 파생클래스를 만들 때 괄호를 붙이고 괄호 안에 기반클래스 이름을 넣는다.
class 기반클래스이름:
코드
class 파생클래스이름(기반클래스이름):
코드
다음은 사람클래스를 만들고 사람클래스를 상속받아 학생클래스를 만든 것이다.
class Person():
def greeting(self):
print('hello')
class Student(Person):
def study(self):
print('do study')
james = Student()
james.greeting()
james.study()
Student 클래스를 만들 때 괄호 안에 기반클래스의 이름인 Person을 넣었다.
Student 클래스에는 greeting메서드가 없지만, Person클래스를 상속받아 Person클래스의 메서드를 호출할 수 있다.
이후 Student클래스만 갖고 있는 study 메서드를 호출하였다.
클래스 상속은 기반 클래스의 기능을 유지하며 새로운 기능을 추가할 수 있다. 클래스 상속은 연관되면서 동등한 개념일 때 사용한다.
클래스의 상속 관계를 확인하려면 issubclass를 사용한다. 기반클래스의 파생클래스가 맞으면 True, 아니면 False를 반환한다.
함수 사용법은 다음과 같다.
issubclass(파생클래스, 기반클래스)
36.2 상속 관계와 포함관계 알아보기
36.2.1 상속관계
위에서 만든 Student 클래스는 Person 클래스를 상속받아 만들어졌다.
학생 Student는 사람 Person과 같은 종류이며 동등한 관계이다.
상속은 명확하게 같은관계이며 동등한 관계일 때 사용한다. 영어로는 is-a 관계라고 부른다.
위 코드의 경우 상속을 사용하지 않고, 속성에 인스턴스를 넣어서 관리하므로 PersonList가 Person을 포함하고 있다. 두 클래스는 동등한 관계가 아니라 포함관계이다. 포함 관계는 영어로 has-a 관계라고 부른다.
같은 종류의 동등한 관계라면 상속을 사용하고, 그 이외에는 속성에 인스턴스를 넣는 포함방식을 사용하면 된다.
36.3 기반 클래스의 속성 사용하기
class Person:
def __init__(self):
print('Person __init__')
self.hello = 'Hello!'
class Student(Person):
def __init__(self):
print('Student __init__')
self.school = 'python dojang'
james = Student()
print(james.school)
print(james.hello)
위 코드를 실행해보면 기반클래스의 hello속성에 접근하려고 하면 에러가 발생한다. 기반 클래스 Person의 __init__메서드가 호출되지 않았기 때문이다. Person클래스의 __init__가 실행되지 않으면 self.hello도 실행되지 않아서 속성이 만들어지지 않는다.
36.3.1 super()로 기반 클래스 초기화하기
super()를 사용하여 기반 클래스의 __init__ 메서드를 호출할 수 있다. 다음과 같이 super() 뒤에 점을 붙여서 메서드를 호출하는 방식이다.
super().메서드
class Person:
def __init__(self):
print('Person __init__')
self.hello = 'Hello!'
class Student(Person):
def __init__(self):
print('Student __init__')
super().__init__()
self.school = 'python dojang'
james = Student()
print(james.school)
print(james.hello)
super().__init__() 처럼 기반 메서드의 __init__ 메서드를 호출해주면 기반 클래스가 초기화되어 속성이 만들어진다. 실행 결과를 보면 Person __init__ 과 Student __init__ 이 둘다 출력되었다.
36.3.2 기반 클래스를 초기화하지 않아도 되는 경우
파생클래스에서 __init__메서드를 생략하면 기반클래스의 __init__이 자동 호출되므로 super()은 사용하지 않아도 된다.
class Person:
def __init__(self):
print('Person __init__')
self.hello = 'Hello!'
class Student(Person):
pass
james = Student()
print(james.hello)
super()은 괄호 안에 파생클래스와 self를 넣어서 현재 클래스가 어떤 클래스인지 명확하게 표시하는 방법도 있다. super()와 기능은 같다.
super(파생클래스, self).메서드
36.4 메서드 오버라이딩 사용하기
메서드 오버라이딩은 파생클래스에서 기반 클래스의 메서드를 새로 정의하는 것이다.
class Person:
def greeting(self):
print('hello')
class Student(Person):
def greeting(self):
print('hello i am student')
james = Student()
james.greeting()
Person 클래스에 greeting메서드가 있는 상태에서 파생클래스인 Student에도 greeting 메서드를 만들었다.
Student의 greeting메서드를 호출하니 hello i am student가 출력되었다.
오버라이딩은 무시하다, 우선하다 라는 뜻을 갖고 있어 기반클래스의 메서드를 무시하고, 파생클래스의 메서드를 출력하였다.
메서드 오버라이딩은 프로그램에서 어떤 기능이 같은 메서드 이름으로 계속 사용되어야 할 때 사용된다.
Person클래스와 Student클래스의 greeting메서드를 보면 hello라는 문구가 중복된다. 이와 같은 경우 오버라이딩 된 메서드에서 super()로 기반클래스의 메서드를 호출하여 재활용하면 중복을 줄일 수 있다.
class Person:
def greeting(self):
print('hello')
class Student(Person):
def greeting(self):
super().greeting()
print('i am student')
james = Student()
james.greeting()
중복되는 기능은 파생 클래스에서 다시 만들지 않고, 기반 클래스의 기능을 사용하면 된다. 메서드 오버라이딩은 원래 기능을 유지하며 새로운 기능을 덧붙일 때 사용한다.
36.5 다중 상속 사용하기
다중 상속은 여러 클래스로부터 상속을 받아서 파생 클래스를 만드는 방법이다. 다음과 같이 클래스를 만들때 괄호 안에 상속받는 클래스 이름을 콤마로 구분하여 넣는다.
class 기반클래스이름1:
코드
class 기반클래스이름2:
코드
class 파생클래스이름(기반클래스이름1, 기반클래스이름2):
코드
다음은 사람클래스와 대학교 클래스를 만든 뒤 다중 상속으로 상속받는 학생 클래스를 만든 것이다.
class Person:
def greeting(self):
print('hello')
class University:
def manage_credit(self):
print('Manage Credit')
class Undergraduate(Person, University):
def study(self):
print('do study')
james = Undergraduate()
james.greeting()
james.manage_credit()
james.study()
Undergraduate 클래스는 기반 클래스로 Person클래스와 University 클래스를 상속받아서 위와 같이 Undergraduate 클래스의 인스턴스로 Person의 greeting과 University의 manage_credit 메서드를 호출할 수 있다.
36.5.1 다이아몬드 상속
class A:
def greeting(self):
print('hello A')
class B(A):
def greeting(self):
print('hello B')
class C(A):
def greeting(self):
print('hello C')
class D(B, C):
pass
x = D()
x.greeting()
기반 클래스 A가 있고, B, C는 A를 상속 받고, D는 다시 B, C를 상속받는다. 그림으로 이 관계를 확인하면 다음과 같다.
클래스 간의 관계가 다이아몬드 처럼 생겨서 다이아몬드 상속 이라고 부른다. A,B,C 모두 greeting라는 메서드를 갖고 있고, A를 상속받은, B,C를 상속받은 D는 어떤 클래스의 메서드를 호출해야할지 매우 애매한 상황에 놓인다. 프로그램에서 이렇게 명확하지 않은 상황은 큰 문제를 일으킬 수 있기 때문에 죽음의 다이아몬드 라고도 불린다.
36.5.2 메서드 탐색 순서 확인하기
파이썬에서 이러한 다이아몬드 상속의 해결책을 제시하기 위해 메서드 탐색 순서(Method Resolution Order, MRO)를 따른다. 클래스 D에서 mro를 사용하면 메서드 탐색 순서가 나온다.
print(D.mro())
MRO에 따르면 D메서드의 호출 순서는 자기자신 D, 그 다음이 B이다. 따라서 D로 greeting를 호출하면 B의 메서드가 호출된다.
파이썬에서 상속 관계가 복잡하게 얽혀있다면 MRO를 살펴보는 것이 좋다.
파이썬에서 object는 모든 클래스의 조상이다. 모든 클래스는 object클래스를 상속받기 때문에 기본적으로 object를 생략했다.
class X(object): # object는 모든 클래스에 기본적으로 상속되어 있어 보통은 생략한다.
pass
36.6 추상 클래스 사용하기
추상클래스는 메서드의 목록만 가진 클래스이며 상속받는 클래스에서 메서드 구현을 강제하기 위해 사용한다.
추상 클래스를 만드려면 import로 abc모듈을 가져와야 한다. 그리고 클래스의 괄호 안에 metaclass=ABCMeta를 지정하고, 메서드를 만들 때 위에 @abstractmethod를 붙여서 추상 메서드로 지정해야 한다.
from abc import *
class 추상클래스이름(metaclass=ABCMeta):
@abstractmethod
def 메서드이름(self):
코드
from abc import *
class StudentBase(metaclass=ABCMeta):
@abstractmethod
def study(self):
pass
@abstractmethod
def go_to_school(self):
pass
class Student(StudentBase):
def study(self):
print('do study')
james = Student()
james.study()
위 코드를 실행하면 다음과 같은 에러가 발생한다. 추상클래스에서 지정한 go_to_school메서드가 상속받은 클래스인 Student에서 구현하지 않았기 때문이다. 추상클래스를 상속받았다면 @abstractmethod가 붙은 추상 메서드를 모두 구현해야 한다. 다음과 같이 코드를 수정하면 에러 없이 실행 가능하다.
from abc import *
class StudentBase(metaclass=ABCMeta):
@abstractmethod
def study(self):
pass
@abstractmethod
def go_to_school(self):
pass
class Student(StudentBase):
def study(self):
print('do study')
def go_to_school(self):
print('go school')
james = Student()
james.study()
james.go_to_school()
추상클래스는 파생 클래스가 반드시 구현해야 하는 메서드를 정해줄 수 있다.
추상클래스의 추상메서드를 다 구현했는지 확인하는 시점은 파생클래스의 인스턴스를 만들 때 이다. 위 코드에서는 james = Student() 코드 부분이다.
36.6.1 추상메서드를 빈 메서드로 만드는 이유
추상 클래스는 인스턴스로 만들 수가 없다. 추상클래스를 인스턴스로 만드려고 하면 에러를 발생한다. 그래서 추상클래스의 추상 메서드는 빈 메서드로 만든다. 인스턴스를 만들 수 없기 때문에 추상메서드를 호출할 일도 없기 때문이다.
추상클래스는 오로지 상속에만, 파생클래스에서 반드시 구현해야 할 메서드를 정해줄 때만 사용한다.
36.7 퀴즈
정답은 c이다.
정답은 d이다.
정답은 a,d이다. 함수 이름을 같게 만드는것이 메서드 오버라이딩이다. 오버라이딩 된 메서드에서도 super()로 기반 클래스의 메서드를 호출할 수 있다.
정답은 b,e이다.
36.8 연습문제: 리스트에 기능 추가하기
정답은 다음 코드와 같다.
class AdvancedList(list):
def replace(self, target, change):
while target in self:
self[self.index(target)] = change
list클래스를 상속받은 클래스에 replace 메서드를 만들어서 리스트에서 바꿀 요소를 지정한 요소로 바꾸는 메서드를 만들었다.
36.9 실습문제: 다중 상속 사용하기
정답은 다음 코드와 같다.
class Bird(Animal, Wing):
def fly(self):
print('날다')
Bird클래스는 Animal 과 Wing 클래스를 상속받으며 fly라는 메서드를 갖는다.
Unit 37. 두 점 사이의 거리 구하기
37.1.1 클래스로 점 구현하기
2차원 평면에서 위치를 표현하려면 x와 y값이 필요하다. 다음과 같이 Point2D로 점을 만드는 클래스를 구현하고 점 두 개를 만들었다.
class Point2D:
def __init__(self, x, y):
self.x = x
self.y = y
p1 = Point2D(x=30, y=20)
p2 = Point2D(x=60, y=50)
print('p1: {} {}'.format(p1.x, p1.y))
print('p2: {} {}'.format(p2.x, p2.y))
2차원 평면에서 두 점을 표시해보면 다음 모양과 같다.
37.1.2 피타고라스의 정리로 두 점의 거리 구하기
두 점의 거리를 구하려면 피타고라스 정리를 이용해야 한다.
피타고라스 정리는 a^2 * b^2 = c^2 이고, p1과 p2로 직각 삼각형을 그리면 다음과 같다.
피타고라스 정리를 이용하려면 먼저 변 a,b의 길이를 알아야 한다. a,b의 길이는 다음과 같다.
a = p2.x - p1.x
b = p2.y - p1.y
피타고라스 정리에서 c의 길이를 구하려면 제곱근을 구해야한다. 파이썬에서 제곱근은 math 모듈의 sqrt 함수를 이용하여 구할 수 있다.
p1과 p2의 거리를 구하는 코드는 다음과 같다.
import math
class Point2D:
def __init__(self, x, y):
self.x = x
self.y = y
p1 = Point2D(x=30, y=20)
p2 = Point2D(x=60, y=50)
a = p2.x - p1.x
b = p2.y - p1.y
c = math.sqrt((a*a) + (b*b))
print(c)
거듭제곱을 구할 때는 math모듈의 pow 함수를 사용해도 된다.
math.pow(값,지수)
c = math.sqrt(math.pow(a, 2) + math.pow(b, 2))
또한 파이썬의 거듭제곱 연산자 **를 사용해도 된다.
c = math.sqrt((a ** 2) + (b ** 2))
절댓값 함수
내장함수 abs 또는 math 모듈의 fabs함수를 사용하면 양수 또는 음수를 절댓값으로 만들 수 있다.
abs(값)
math.fabs(값)
namedtuple 사용하기
collections모듈의 namedtuple함수를 사용하여 각 요소에 이름을 지정해 줄 수 있다. namedtuple은 자료형 이름과 요소의 이름을 지정하면 클래스를 생성해준다. 자료형 이름은 문자열, 요소의 이름은 문자열 리스트로 넣어줘야 한다.
클래스 = collections.namedtuple('자료형이름', ['요소이름1', '요소이름2'])
namedtuple로 생성한 클래스는 값을 넣어서 인스턴스를 만들 수 있다. 인스턴스.요소이름 또는 인스턴스[인덱스]형식으로 요소에 접근할 수 있다.
다음은 namedtuple를 이용하여 두 점 사이의 거리를 구한 것이다.
import math
import collections
Point2D = collections.namedtuple('Point2D', ['x', 'y'])
p1 = Point2D(x=30, y=20)
p2 = Point2D(x=60, y=50)
a = p1.x - p2.x
b = p1.y - p2.y
c = math.sqrt((a * a) + (b * b))
print(c)
37.2 연습문제: 사각형의 넓이 구하기
정답은 다음 코드와 같다.
wid = abs(rect.x2 - rect.x1)
hei = abs(rect.y2 - rect.y1)
area = wid * hei
높이와 밑변의 길이는 경우에 따라 음수가 될수 있기 때문에 abs함수를 이용하여 절댓값으로 만들었다.
37.3 두 점 사이의 거리 구하기
정답은 다음 코드와 같다.
length = 0
for i in range(len(p)-1):
a = p[i+1].x - p[i].x
b = p[i+1].y - p[i].y
length += math.sqrt((a*a) + (b*b))
람다 표현식은 식 형태로 되어 있다고 해서 람다 표현식 이라고 부른다. 함수를 간편하게 작성할 수 있어서 다른 함수의 인수로 넣을 때 주로 사용한다.
32.1 람다 표현식으로 함수 만들기
다음은 숫자를 받은 뒤 10을 더해서 반환하는 함수 이다.
람다 표현식은 lambda에 매개변수를 저장하고 :(클론) 뒤에 반환 값으로 사용할 식을 지정한다. 이 함수를 람다 표현식으로 작성하면 다음과 같다.
실행을 해보면 함수 객체가 나오고, 이 상태로는 함수를 호출할 수 없다. 람다 표현식은 이름이 없는 익명 함수이기 때문이다. lamda로 만든 익명 함수를 호출하려면 다음과 같이 람다 표현식을 변수에 할당해야 한다.
위 람다 표현식은 매개변수로 x를 받고, x에 10을 더해 반환한다는 뜻이다.
32.1.1 람다 표현식 자체를 호출하기
람다표현식은 변수에 할당하지 않고, 람다표현식 자체를 괄호로 묶고, 다시 괄호를 붙여 괄호 안에 인수를 넣어 호출할 수 있다.
32.1.2 람다 표현식 안에서는 변수를 만들 수 없다
람다 표현식은 변수 표현식 안에서 새 변수를 만들 수 없다. 반환값 부분은 변수의 생성 없이 한 줄로 표현할 수 있어야 한다. 새 변수를 만드는 코드를 넣으면 에러를 발생한다.
표현식 밖에서 선언된 변수는 사용할 수 있다.
31.1.3 람다 표현식을 인수로 사용하기
def로 만든 함수에서 map을 사용하여 리스트를 plus_ten() 함수에 넣으면 매개변수에 10을 더해서 반환하므로 리스트의 각 요소들은 10씩 더해진다.
map의 함수 자리에 람다 표현식을 넣어도 된다.
람다 표현식을 사용하면 세줄이였던 위의 코드를 1줄로 줄일 수 있다.
람다 표현식으로 매개변수가 없는 함수를 만들때는 lamba뒤에 아무것도 지정하지 않고 클론을 붙인 후 반환값을 지정한다.
32.2 람다 표현식과 map, filter, reduce 함수 사용하기
32.2.1 람다 표현식에 조건부 표현식 사용하기
람다 표현식에 조건부 표현식을 사용하는 방법은 다음과 같다.
lambda 매개변수들: 식1 if 조건식 else 식2
다음은 map을 사용하영 3의 배수를 문자열로 변환하는 것이다.
리스트의 요소를 각각 처리했기 때문에 반환값도 요소로 나온다. 요소가 3의 배수일 때는 문자열로 변환하였다.
람다 표현식 안에서 조건부 표현식(if, else)을 사용할 때는 클론을 붙이지 않는다. 조건부 표현식은 '식1 if 조건식 else 식2' 형식으로 사용하며 조건식이 참이면 식1을, 거짓이면 식2 를 사용한다. 람다 표현식에서 if를 사용하면 반드시 else를 사용해야 한다. else를 사용하지 않으면 에러가 발생한다.
람다 표현식에서는 elif를 사용할 수 없다. 따라서 람다 표현식에서는 다음과 같이 if를 연달아서 사용해야 한다.
lambda 매개변수들: 식1 if 조건식1 else 식2 if 조건식2 else 식3
다음은 리스트에서 1은 문자열로 변환하고, 2는 실수로 변환하고, 3이상의 수는 10을 더하는 코드이다.
식이 알아보기 복잡하기 때문에 이와 같은 경우에는 코드의 길이가 길어지더라도 알아보기 쉽게 작성하는것이 좋다.
32.2.2 map에 객체를 여러 개 넣기
map은 리스트 등 반복 가능한 객체를 여러개 넣을 수 있다. 다음은 두 리스트의 요소들을 곱해서 새로운 리스트를 만든다.
리스트 두개를 처리할 때는 람다 표현식에서 매개변수를 두 개 지정하고, 리스트 두 개를 콤마로 구분해서 넣어준다. 람다 표현식의 매개변수의 개수에 맞게 반복 가능한 객체도 콤마로 구분하여 넣어주면 된다.
32.2.3 filter 사용하기
filter는 반복 가능한 객체에서 특정 조건에 맞는 요소만 가져온다. filter에 지정한 함수가 True를 반환할 때만 해당 요소를 가져온다.
다음은 def로 함수를 만들어 리스트에서 5보다 크고 10보다 작은 숫자만 가져오는 함수이다.
함수 f를 람다표현식으로 만들면 다음과 같다.
람다 표현식에 조건식을 넣어 True인 요소만 가져오도록 하였다.
32.2.4 reduce 사용하기
reduce는 반복 가능한 객체의 각 요소를 지정된 함수로 처리한 뒤 이전 결과와 누적하여 반환하는 함수이다.
reduce는 내장함수가 아니라 functools모듈에 있다.
다음은 리스트에 저장된 요소를 순서대로 더한 뒤 누적한 결과를 반환하는 코드이다.
함수 f를 람다 표현식으로 만들어 reduce에 넣으면 다음과 같다.
매개변수 x,y를 지정하고 x와 y를 더한 값을 반환하도록 하였다.
32.3 퀴즈
정답은 d 이다.
정답은 b이다.
정답은 c이다.
32.4 연습문제: 이미지 파일만 가져오기
정답은 다음 코드와 같다.
list(filter(lambda x: x.find('jpg') != -1 or x.find('png') != -1, files))
find()메서드는 찾는 문자열이 있으면 그 문자열의 인덱스를 반환하고, 없으면 -1을 반환한다. 둘중 하나만 참이 되면 되므로 or연산자를 사용하여 람다 표현식으로 조건식이 True 가 나오는 요소들을 가져왔다.
32.5 심사문제: 파일 이름을 한꺼번에 바꾸기
정답은 다음 코드와 같다.
list(map(lambda x : '{0:03d}.{1}'.format(int(x.split('.')[0]), x.split('.')[1]), files))
files의 각 요소들을 .을 기준으로 나누어 숫자는 0을 채운 3자리로 만들어 다시 확장자를 더해서 리스트에 담았다.
Unit 33. 클로저 사용하기
33.1 변수의 사용 범위 알아보기
파이썬 스크립트에서 변수를 만들면 다음과 같이 함수 안에서도 사용할 수 있다.
x = 10
def foo():
print(x)
foo()
print(x)
위 변수 x처럼 함수를 포함하여 스크립트 전체에서 접근할 수 있는 변수를 전역변수 라고 하며, 전역 변수에 접근할 수 있는 범위를 전역 범위라 한다.
x를 함수 안에 지정하면 함수 밖에서는 x를 출력할 수 없다.
def foo():
x = 10
print(x)
foo()
print(x)
x가 정의되지 않았다는 에러가 발생한다. 변수 x는 함수 안에서 만들어 졌기 때문에 함수 안에서만 접근할 수 있는 지역변수이다. 지역변수를 접근할 수 있는 범위를 지역 범위라고 한다.
33.1.1 함수 안에서 전역 변수 변경하기
x = 10
def foo():
x = 20
print(x)
foo()
print(x)
위 코드는 함수 안에서 x의 값을 20으로 변경했지만, 함수 밖에서 출력하면 10이 나왔다. 함수 안의 x는 그 함수의 지역 변수이다. 함수 밖의 x와 함수 안의 x는 이름만 같을 뿐 서로 다른 변수이다.
함수 안에서 전역변수의 값을 변경하려면 global이라는 키워드를 사용해야 한다.
x = 10
def foo():
global x
x = 20
print(x)
foo()
print(x)
함수 안에서 변수를 global로 지정하면 전역 변수로 사용할 수 있고, 해당 전역 변수가 없으면 해당 변수는 전역변수가 된다.
def foo():
global x
x = 20
print(x)
foo()
print(x)
네임 스페이스
파이썬에서 변수는 네임 스페이스에 저장된다. locals함수를 사용하면 현제 네임스페이스를 딕셔너리 형태로 출력할 수 있다.
다음과 같이 함수 내에서 locals함수를 사용하면 함수의 지역 범위 내의 네임스페이스를 출력할 수 있다.
print_hello 함수 안에 print_message 함수를 만들었다. hello변수는 print_hello 안에 있으며, print_message함수에서 hello변수를 출력한다. print_hello 함수 안에서는 print_message 함수를 실행한다. 두 함수가 실제로 동작하려면 print_hello를 호출하면 print_hello > print_message 순서로 실행된다.
33.2.1 지역변수의 범위
위의 코드에서 안쪽 함수 print_message에서는 바깥쪽 함수 print_hello의 지역변수 hello를 사용할 수 있다. 바깥쪽 함수의 지역 변수는 그 안에 속한 모든 함수에서 접근할 수 있다.
33.2.2 지역변수 변경하기
def A():
x = 10
def B():
x = 20
B()
print(x)
A()
A함수에서 변수 x에 10을 할당하고, A안의 B함수에서 변수 x에 20을 할당하고 B를 호출하고 x를 출력하는 A함수를 실행했다. 결과값은 10이 나왔다. B함수에서 바깥쪽 함수의 지역변수 x를 변경하는 것이 아니라 안쪽 함수 B에서 이름이 같은 지역변수 x를 새로 만드는 것이다. 파이썬에서는 함수에서 변수를 만들면 항상 현재 함수의 지역변수가 된다.
현재 함수 바깥쪽에 있는 지역변수의 값을 변경하려면 nonlocal 키워드를 사용해야 한다.
def A():
x = 10
def B():
nonlocal x
x = 20
B()
print(x)
A()
nonlocal키워드로 바깥쪽 함수의 지역변수의 값을 변경한 것을 확인할 수 있다.
nonlocal은 현재 함수의 지역변수가 아니라는 뜻이기 때문에 바깥쪽 함수의 지역변수를 사용한다.
33.2.3 nonlocal이 변수를 찾는 순서
def A():
x = 10
y = 100
def B():
x = 20
def C():
nonlocal x
nonlocal y
x += 30
y += 300
print(x)
print(y)
C()
B()
A()
함수 C에서 nonlocal 키워드를 사용하게 되면 변수 x는 바깥쪽의 b 함수의 지역변수 x = 20을 사용하게 된다. 변수 y는 B 함수에는 없기 때문에 한단계 더 바깥으로 나가서 A 함수의 y 변수를 사용하게 된다. nonlocal은 가까운 함수부터 지역변수를 찾고, 지역변수가 없으면 계속 밖으로 나가서 찾는다.
nonlocal 키워드를 사용했는데, 바깥쪽 함수에 nonlocal로 지정한 변수가 없다면 위와 같은 에러를 발생시킨다.
33.2.4 global로 전역변수 사용하기
x = 1
def A():
x = 10
def B():
x = 20
def C():
global x
x += 30
print(x)
C()
B()
A()
함수가 몇단계든 상관없이 global키워드를 사용하면 무조건 전역변수를 사용하게 된다. 함수 C에서만 전역변수를 사용했기 때문에 x+=30 은 31이된다.
33.3 클로저 사용하기
다음은 함수 바깥 쪽에 있는 지역변수 a,b를 사용하여 a * x + b를 계산한 뒤 반환한다.
def calc():
a = 3
b = 5
def mul_add(x):
return a * x + b
return mul_add
c = calc()
print(c(1), c(2), c(3), c(4), c(5))
calc함수에 변수 a,b를 만들고 3과 5를 저장하였다. mul_add 함수에서는 a 와 b를 사용하여 a * x + b를 반환한다. 이후 calc 함수에서 mul_add 함수 자체를 반환한다. 함수를 반환할 때는 함수 이름만 반환하며 괄호를 붙이지 않는다.
함수 calc를 호출하고 반환값을 c에 저장하면 calc에서 mul_add를 반환했으므로 c에는 함수 mul_add가 들어간다.
print(c)를 통해 c를 출력해보면 mul_add함수 자체만 들어간 것을 알 수 있다.
그리고 c에 숫자를 넣어 호출하면 계산식에 따라 연산한 값이 출력된다.
함수 calc가 끝났는데도, calc의 지역변수 a,b를 사용하여 계산을 하고 있다. 이렇게 함수를 둘러싼 환경을 유지하다가 함수를 호출할 때 다시 꺼내서 사용하는것을 클로저 라고 한다.
c에 저장된 함수가 클로저 이기 때문에 위 그림처럼 calc함수 내의 지역변수를 가져와서 사용할 수 있다.
클로저를 사용하면 프로그램의 흐름을 변수에 저장할 수 있고, 지역변수와 코드를 묶어서 사용하고 싶을 때 활용한다.
또한 클로저에 속한 지역변수는 밖에서 직접 접근할 수 없기 때문에 데이터를 숨기고 싶을때 사용한다. 함수 외부에서 데이터의 변경이 일어나면 안될 때 사용해도 좋을것 같다.
33.3.1 lambda로 클로저 만들기
클로저는 다음과 같이 람다로도 만들 수 있다.
def calc():
a = 3
b = 5
return lambda x: a * x + b
c = calc()
print(c(1),c(2),c(3))
람다는 이름이 없는 익명함수 이고, 클로저는 함수를 둘러싼 환경을 유지했다가 나중에 다시 사용하는 함수를 뜻한다.
33.3.2 클로저의 지역변수 변경하기
클로저의 지역변수를 변경하고 싶으면 다음과 같이 nonlocal을 사용한다.
def calc():
a = 3
b = 5
total = 0
def mul_add(x):
nonlocal total
total = total + a * x + b
print(total)
return mul_add
c = calc()
c(1)
c(2)
c(3)
a * x + b의 결과를 calc함수의 지역변수 total에 누적한다.
33.4 퀴즈
정답은 b,d이다. 함수 안에서 전역변수의 값을 바꿀 때는 global을 사용하고, 함수 안에 함수가 여러 단계 있어도 전역변수를 사용할 수 있다.
정답은 c,d이다. 지역변수는 함수 바깥쪽에서 사용할 수 없고, nonlocal을 이용하면 두단계 이상 바깥쪽에 있는 지역변수도 사용할 수 있다.
정답은 c,d이다. 람다는 익명함수를 의미하므로 다른 의미이다. 클로저는 람다 표현식으로 만들 수 있다.
33.5 연습문제 : 호출 횟수를 세는 함수 만들기
정답은 다음코드와 같다.
nonlocal i
i += 1
return i
return count
conunt 함수에서 counter 함수의 지역변수 i를 사용하여 counte 함수가 실행될 때마다 1씩 누적하고 누적한 값을 반환하며 counter 함수에서 count함수를 반환하게 하였다.
33.6 심사문제: 카운트 다운 함수 만들기
정답은 다음 코드와 같다.
i = n+1
def down():
nonlocal i
i -= 1
return i
return down
입력한 값을 포함하여 숫자를 세야 하므로 countdown 함수의 지역변수 i를 입력값보다 1 크게 하고, down함수를 만들어 i를 1씩 감소시킨 후 반환하였다. countdown 함수는 down 함수를 반환하였다.
Unit 34. 클래스 사용하기
클래스는 객체를 표현하기 위한 문법이다. 클래스는 게임속의 다양한 직업들, 웹 브라우저 에서 사용하는 스크롤바, 버튼, 체크박스 등 같은 개념을 사용하는 객체를 묶은 것이다. 객체는 스크롤바, 버튼, 체크박스처럼 특정한 개념이나 모양으로 존재하는 것이다. 프로그래밍에서 객체를 만들 때 클래스를 사용한다.
게임의 캐릭터를 클래스로 표현하면 캐릭터는 체력, 마나, 공격력 등이 필요하며 칼로 찌르기, 베기 등의 스킬이 있어야한다.
체력, 마나, 공격력 등을 속성(attribute)라 하고, 찌르기, 베기 등의 스킬을 메서드(method)라 한다.
이런 프로그래밍 방법을 객체 지향 프로그래밍이라고 한다. 객체 지향 프로그래밍은 복잡한 문제를 잘게 나누어 객체로 만들고, 객체를 조합하여 문제를 해결한다. 기능을 개선하고 발전시킬때도 해당 기능을하는 클래스만 수정하면 되서 유지보수도 효율적이다.
지금까지 사용했던 숫자, 문자, 리스트, 딕셔너리 등도 다 각각의 객체이다.
34.1 클래스와 메서드 만들기
클래스는 class에 이름을 지정하고 클론을 붙인 뒤 다음줄 부터 def로 메서드를 작성하면 된다. 메서드는 클래스 안에 들어있는 함수들이다.
파이썬에서는 보통 클래스 이름은 대문자로 시작한다. 메서드 작성 방법은 함수와 같고, 코드는 반드시 들여쓰기를 해야 한다. 메서드의 첫번째 매개변수는 반드시 self를 지정해야 한다.
class 클래스이름:
def 메서드(self):
코드
다음은 간단한 사람 클래스 이다.
다음과 같이 클래스에 괄호를 붙인뒤 변수에 할당하면 사용할 수 있다.
변수 james를 클래스의 인스턴스라고 한다. 클래스는 특정 개념만 표현할 뿐 사용하려면 인스턴스를 생성해야 한다.
34.1.1 메서드 호출하기
메서드는 인스턴스를 통해 호출된다. 다음과 같이 인스턴스 뒤에 .(점)을 붙이고 메서드를 호출하면 된다.
위와 같이 인스턴스를 통해 호출하는 메서드를 인스턴스 메서드라고 인스턴스 메서드 라고 부른다.
34.1.2 파이썬에서 흔히 볼 수 있는 클래스
지금까지 사용한 int, list, dict등도 클래스 이다. 이 클래스로 인스턴스를 만들고, 메서드를 사용한 것이다.
위와 같은 코드들은 int 클래스로 인스턴스 a를 만들고, list클래스로 인스턴스 b를 만들고, dict클래스로 인스턴스 c를 만든 것이다. 정수를 만들 땐 int를 생략하고, 리스트, 딕셔너리는 [ ], { }을 이용하여 만들지만 이것들도 다 클래스 이다.
위 코드는 인스턴스 b에서 append라는 메서드를 호출하여 사용한 것이다.
파이썬에서는 자료형도 각각의 클래스이다. type을 사용하면 객체(인스턴스)가 어떤 클래스 인지 확인할 수 있다.
34.1.3 인스턴스와 객체의 차이점
객체와 인스턴스는 같은 것이다. 보통 객체만 지정할 때는 객체라고 부르고, 클래스와 연관지어서 말할 때는 인스턴스라고 부른다.
빈 클래스 만들기
빈 클래스는 다음과 같이 코드 부분에 pass를 넣어주면 된다.
class Person:
pass
메서드 안에서 메서드 호출하기
메서드 안에서 메서드를 호출할 때는 self.메서드() 형식으로 호출해야 한다. self없이 메서드 이름만 사용하면 클래스 바깥쪽에 있는 함수를 호출한다는 뜻이 된다.
class Person:
def greeting(self):
print('hello')
def hello(self):
self.greeting()
특정 클래스의 인스턴스인지 확인하기
현재 인스턴스가 특정 클래스의 인스턴스인지 확인할 때는 isinstance함수를 사용한다. 특정 클래스의 인스턴스가 맞으면 True, 아니면 False를 반환한다.
isinstance는 주로 객체의 자료형을 판단할 때도 사용한다.
34.2 속성 사용하기
속성을 만들 때는 __init__메서드 안에서 self.속성 에 값을 할당한다.
class 클래스이름:
def __init__(self):
self.속성 = 값
class Person:
def __init__(self):
self.hello = 'hello! '
def greeting(self):
print(self.hello)
james = Person()
james.greeting()
__init__ 메서드는 james = Person() 처럼 클래스에 괄호를 붙여서 인스턴스를 만들 때 호출되는 특별한 메서드이다. __init__메서드는 인스턴스(객체)를 초기화 한다.
__init__처럼 앞뒤로 __(밑줄두개)가 붙은 메서드는 파이썬이 자동으로 호출해주는 메서드로 스페셜 메서드 또는 매직메서드 라고 부른다.
greeting메서드 에서는 print로 self.hello를 출력하였다.
Person으로 객체를 만들고, greeting를 호출하면 self.hello에 저장된 hello! 가 출력된다.
속성은 __init__메서드 안에서 만들고, self에 .(점)을 붙여서 할당한다. 클래스 안에서 속성을 사용할 때도 self에 .(점)을 붙여서 사용한다.
34.2.1 self의 의미
self는 인스턴스 자기 자신을 의미한다. 인스턴스가 생성될 때 self.hello처럼 자기 자신에 속성을 추가하였다.
위 클래스에서 __init__의 매개변수 self에 들어가는 값은 Person이다. self가 완성된 뒤에는 james가 할당된다. 이후 메서드를 호출하면 현재 인스턴스가 자동으로 매개변수 self에 들어온다. 그래서 greeting에서 self.hello의 속성을 출력할 수 있었던 것이다.
위 설명을 그림으로 나타내면 다음과 같다.
34.2.2 인스턴스를 만들 때 값 받기
다음과 같이 __init__메서드에서 self 다음에 값을 받을 매개변수를 지정하고, 매개변수를 self.속성에 넣어준다.