반복문과 제어문, 표준입출력

시작하며

반복문과 제어문, 함수와 표준입출력에 대해 알아보자.

반복문과 제어문

if, for, while, continue, break 이 단어들은 앞으로 파이썬을 프로그래밍하며 수 없이 많이 보게될 단어들이다. 여러가지 반복 작업을 수행하기 위해 반복문을 사용하게 될 것이며, 함수를 실행하는데 있어 조건을 걸기 위해 조건문을 사용하게 될 것이다.

if

if는 직역하자면 ‘만약에’라는 뜻이다. ‘만약에~ 라면 어떨까?’의 문장으로 쓰이곤한다. 프로그램에서도 마찬가지로 if는 ‘만약,’ 이라는 뜻으로 ‘학생’이라면, ‘등급이 무엇’이라면 등의 조건을 달고 만약 그 조건이 참이거나 거짓이면 if문을 실행하는 식의 형태로 이루어진다.

1
2
if 조건:
    실행 명령문

날씨에 대한 프로그램을 만들고, 그 날의 날씨를 입력받으면 날씨에 대한 조언을 해주는 프로그램을 만든다고 가정해보자.

1
2
3
4
5
6
7
8
9
10
weather = input("오늘의 날씨는 어떤가요? : ")

if weather == "맑음" :
    print("날씨가 좋습니다.")

if weather == "흐림" :
    print("날씨가 흐립니다. 우산을 챙기세요")

if weather == "미세먼지" :
    print("미세먼지가 많습니다. 마스크를 착용하세요")

하지만 이렇게 하면 하나의 질문을 처리하기위해 많은 조건문을 사용해야하고 예외의 상황을 처리하지못한다. 이럴 때 사용하기좋은게 elif 와 else이다. elif는 if 조건문에 다른 조건을 걸 수 있고, else는 if 조건문들의 어느 조건에도 속하지 않을때 실행되는 문이다.

1
2
3
4
5
6
if weather == "맑음":
     print("날씨가 좋습니다")
elif weather == "미세먼지":
     print("마스크를 챙기세요")
else:
     print("우산을 챙기세요")

이렇게 하면 맑을때는 날씨가 좋다는 이야기를, 미세먼지가 꼈을 때는 마스크를 챙기라는 이야기를, 그 외의 날씨에는 우산을 챙기라는 프로그램을 만들 수 있다.

for

가게의 대기 손님 프로그램을 만든다고 가정해보자. 우리들은 손님 1명이 올 때마다 1개의 대기번호를 추가한다.

1
2
3
print("대기번호 : 1")
print("대기번호 : 2")
...

10명 내외일 때에는 직접 타이핑할 수 있었지만, 만약 수백 수천명의 사람들이 가게를 방문한다면? 이 때 필요한 것이 for문이다.

1
2
for 변수 in 반복대상:
    실행 명령문

식의 문을 작성하면된다. for문을 활용해 대기번호를 작성해보자

1
2
3
4
5
6
7
8
9
10
11
people_num = {1, 2, 3, 4, 5}

for waiting_num in people_num: #people_num의 수를 차례대로 waiting_num에 집어넣는다.
  print("대기 번호 : {}".format(waiting_num))

결과:
대기 번호 : 1
대기 번호 : 2
대기 번호 : 3
대기 번호 : 4
대기 번호 : 5

사람들의 수(people_num) 만큼 실행되는 모습을 볼 수 있다.

반복대상에는 이렇게 변수를 직접 넣어줄 수도 있고, range(범위1, 범위2)를 넣어줄 수도, 직접 숫자를 넣어줄 수도 있다.

for문 한 줄로 사용하기

for 문은 간결하게 한 줄로도 사용할 수 있다.

1
[실행문 for 변수 in 반복대상]

예를 들어보자. 출석번호가 1, 2, 3, … 인 번호 프로그램을 101, 102, 103 순으로 바꾼다고 해보자. 그 때

1
2
3
students = [1, 2, 3, 4, 5]
students = [i + 100 for i in students] #students 로부터 값을 하나씩 얻어와서 i에 저장하고 i+100을 해서 students에 집어넣기
print(students)

을 해주면 아주 간결하게 반복문을 사용할 수 있다.

while

while 또한 반복문이지만, for과 while은 비슷하면서도 다르다. for문은 반복 대상에서 값을 하나씩 꺼내서 반복 작업을 수행하지만, while문은 조건이 만족하는 동안 끝 없이 반복한다.

while문은 다음과 같이 작성한다

1
2
while 조건:
    실행 명령문

카페에서 사람을 3번 부르고, 3번 불러도 응답이 없으면 커피를 버리는 시스템을 만든다고 생각해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
customer = "토르" #손님의 이름
index = 3 #반복해서 부를 숫자

while index >= 1:
    print("{}님, 커피가 완성되었습니다.".format(customer))
    index -= 1 #반복 횟수를 줄임
    if index == 0: #전부 불렀다면
      print("{}님, 커피는 폐기되었습니다.".format(customer))

결과:
토르님, 커피가 완성되었습니다.     
토르님, 커피가 완성되었습니다.     
토르님, 커피가 완성되었습니다.     
토르님, 커피는 폐기되었습니다.

무자비한 카페 정책이다 while index >= 1: 반복해서 부를 숫자가 1 이상이라면 while문을 실행하겠다는 뜻이다.

index -= 1 이 구문이 없으면 while문은 계속해서 커피가 완성되었다는 문구를 날릴 것이다. 조건을 정해주는 것

if index == 0: index -= 1을 반복해서 실행해 0이 되고, while문은 빠져나가진다. 그리고 index가 == 0 이라면 if 문을 실행한다.

무한루프

만약 index -= 1 구문이 없었다면 어떻게 되었을까? ‘토르님, 커피가 완성되었습니다.’ 라는 구문이 카페 내에서 24시간 전기가 나갈 때 까지 방송될 것이다. 우리들은 이렇게 조건이 없는 반복문을 ‘무한루프’라고 부르기로 했다. 만약 디버깅상태에서 무한루프에 걸렸다면 Ctrl + C를 누르면 빠져나올 수 있다.

continue, break

continue와 break는 반복문의 흐름을 제어한다. continue는 continue 아래 줄의 명령을 수행하지않고 다음 반복대상으로 넘어갈 때 사용하며 break 는 즉시 반복문을 탈출하는데 쓰인다.

‘선생님이 차례대로 수행평가를 한다’라는 시나리오의 프로그램을 만들었을 때, 결석하는 학생들은 수행평가를 진행할 수 없을 것이다. 이럴 때 사용하는 것이 continue이며, 출석번호 9번의 녀석이 양아치라서 수행평가를 하지 않고 버텨서 선생님이 화가 날 때, 수행평가는 중지될 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
absent = [2, 5] #결석한 학생번호

for i in range(1, 20):
    if i in absent:
       print("{}번은 결석이로군, 다음차례".format(i))
       continue
    print("{}번, 너 차례다.".format(i))
    if i == 9:
       print("안해? 교무실로 따라와 {}번.".format(i))
       break

결과:
1,  차례다.
2번은 결석이로군, 다음차례   
3,  차례다.
4,  차례다.
5번은 결석이로군, 다음차례   
6,  차례다.
7,  차례다.
8,  차례다.
9,  차례다.
안해? 교무실로 따라와 9.

무한루프에 빠질 수 있는 상황에 continue와 break 를 적절히 사용해 흐름을 제어하는게 훌륭한 프로그래밍의 기초라 할 수 있겠다.

함수

학생 때 ‘함수’라는 파트를 공부하면 한 번쯤은 봤을 법한 이 그림이 함수의 전부라고도 할 수 있다.

image

X로부터 입력받은 값을 Function f:로 처리하고 처리한 값은 f(x)로 내보낸다는 간결한 그림이다.

이 함수는 프로그래밍에서도 쓰이는데, 우리가 직접 만들지 않아도 처음부터 사용해왔던 print(), len(), append(), pop()등이 전부 다 함수이다. 그렇다면 우리가 직접 만든다하면 함수는 어떻게 정의해야할까?

함수를 정의하는 방법

1
2
def 함수이름(인자):
    실행명령문

함수는 def 함수이름(인자 값1, 인자 값2, …): 으로 선언하고 이 함수를 호출했을 시 그 아래의 실행명령문을 처리한다.

전달값, 반환값

함수는 def 함수이름(인자): 의 ‘인자’로 값들을 전달받고 함수의 실행명령문을 처리한다음 그 값들을 반환 한다.

1
2
3
def 함수이름(인자값1, 인자값2, 인자값3, ...)"
    실행명령문
    return 반환값

함수를 처리하는 과정의 그림과 비슷한 구조를 띄기 시작했다. 우리들은 x 대신 ‘인자값들’ 로 값을 전달받고 실행명령문으로 처리한 후 y 대신 ‘반환값’으로 값을 내보낸다.

기본값

만약 서울의 어느 한 고등학교에 1학년 학생들을 관리하기 위해 students 라는 함수를 만들고, 학생들의 정보를 입력받는다고 쳐보자. 당연하게도 이 들은 기본적으로 17세일 것이며(국제 표준 나이 적용 전), 서울에 살고 있을 것이다! 우리들은 이렇게 대부분의 경우를 차지할 때 ‘기본값’을 지정해줄 수 있다.

1
2
3
4
5
6
7
8
9
def students(name, age=17, livein="SEOUL"):
    print("이름 : {}, 나이 : {}, 사는 곳 : {}".format(name, age, livein))

students(홍길동)
students(김철수)

결과:
이름 : 홍길동, 나이 : 17, 사는  : SEOUL
이름 : 김철수, 나이 : 17, 사는  : SEOUL

‘그런데 서울에 안 살수도 있고, 조기입학을 할 수도, 빠른 일수도 있지 않나요?’ 맞는 말이다. 그럴 때는 전달값이 name, age, livein 이고 기본값을 설정해준 것 뿐이기 때문에

1
2
3
4
5
6
7
def students(name, age=17, livein="SEOUL"):
    print("이름 : {}, 나이 : {}, 사는 곳 : {}".format(name, age, livein))

students("김외곽", 16, "SEJONG-SI")

결과:
이름 : 김외곽, 나이 : 16, 사는  : SEJONG-SI

정상적으로 나오는 모습을 확인할 수 있다.

키워드 인자

굳이 순서를 맞춰서 전달값을 보내주지 않고도 위 함수를 사용할 수 있다.

1
2
3
4
5
6
7
def students(name, age=17, livein="SEOUL"):
    print("이름 : {}, 나이 : {}, 사는 곳 : {}".format(name, age, livein))

students("김외곽", livein="SEJONG", age=16)

결과:
이름 : 김외곽, 나이 : 16, 사는  : SEJONG-SI

이렇게 전달값으로 할당한 변수를 직접 써주고 할당해주면 끝난다. 키워드 인자는 보통 기본값들이 잘 정의되어 있고 필요한 부분을 변경할 때 많이 쓰인다.

가변 인자

전달값이 들쑥날쑥하는 경우에는 어떻게 할까? 예를 들어 배운 언어의 개수를 등록하는 프로그램이라고 할 때, 유재석씨는 5개의 언어를, 김태호씨는 3개의 언어를 배웠다고 할 때 ‘가변 인자’를 배우지 않은 상태에서는 이렇게 작성해야 할 것이다.

1
2
3
4
5
6
def profile(name, age, lang1, lang2, lang3, lang4, lang5):
    print("이름 : {}\n나이 : {}\n".format(name, age))
    print(lang1, lang2, lang3, lang4, lang5)

profile("유재석", 20, "Python", "Java", "C", "C#", "Javascript")
profile("김태호", 26, "Python", "Java", "", "", "")

그런데 만약 유재석 씨가 언어를 더 배운다면? 함수의 전달 값과 함수의 내용 전부 수정해줘야 할 것이고 유저들이 많아질 수록 번거롭고 힘들어질 것이다. 그럴 때 우리는 ‘가변인자’ 를 활용한다.

1
2
3
4
5
6
7
8
9
def profile(name, age, *language): #전달값에 *만 추가해주면 끝이다.
    print("이름 : {}\n나이 : {}\n".format(name, age))
    
    for lang in language:
        print(lang, end="") #언어들을 한 줄에 표시
        print("") #줄바꿈

profile("유재석", 20, "Python", "Java", "C", "C#", "Javascript")
profile("김태호", 26, "Python", "Java")

for 문을 활용해서 가변인자 language의 값을 하나씩 받아서 출력해주었다.

지역변수, 전역변수

지역과 전역이라는 말은 사전적 의미로 ‘다른 곳과 구별되는 지표상의 공간적 범위’ 와 ‘어느 지역의 전체’를 뜻한다. 쉽게 말해 지역은 어느 ‘부분’ 이고 전역은 ‘전체’를 뜻한다. 지역변수와 전역변수도 단어의 뜻에 맞게 사용되는데, 지역변수는 ‘함수만의 변수’, 전역변수는 ‘프로그램에 직접 선언된 변수’라고 생각하면 편하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
num = 3
def numprint(num2):
    num =5
    print("{}".format(num))
    num2 += 1
    print("{}".format(num2))

numprint(num)
print(num)

결과:
5
4
3

numprint 의 num은 지역변수 num의 값이다(전역변수 num과는 별개로 침). num=5를 해주었으나 전역변수 num과는 달라 새로운 지역변수 num=5가 나오게되고 새 num 5 와 전달받은 num의 값에 +1을 더한 값이 나오는 것이다. 전달받은 num2에 +1을 하여 +4가 될 것 같지만 함수를 빠져나오며 num=3 인채로 존재한다. 함수 내에서 지역변수 대신 전역변수를 사용하려면 global 키워드를 사용해주면 된다.

전역변수를 많이 사용하게 되면 프로그램이 복잡해지고 어려워진다. 그렇기에 우리는 가급적 전역변수를 사용하기보다는 값을 반환(return)해 사용하는 방식을 사용해야한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
num = 3
def numprint(num):
    print("{}".format(num))
    num += 1
    print("{}".format(num))
    return num

numprint(num) #3과 4 출력
print(num) #3 출력
print("") # 구분하기 위한 엔터
print(numprint(num))

결과:
3
4
3

3
4
4
  1. numprint(num)에 의해 전달받은 값 3과 3+1=4 를 print하고, return num 으로 num 값 4를 반환한다.
  2. 하지만 반환한 num값을 쓰일 곳이 없으니 4가 나오지 않았고 print(num)에 의해 전역변수 num의 값 3이 나온다.
  3. print(numprint(num))에 괄호안의 numprint(num)이 먼저 처리되었고, 3과 4가 print되고 return 받은 값 4가 다시 print 되어 3 4 4 가 나오게 된다.

표준 입출력

sep

print 문에는 비밀이 하나 숨겨져있다. ‘,’ 를 사용하면 한 칸이 띄어진 채 문자열이 구분되고 ‘+’ 를 사용하면 띄어지지않은 채 문자열이 써진다. 바로 문자열들을 구분하는 ‘,’ 기호의 기능 띄어쓰기’ ‘를 설정해줄 수 있다는 것이다.

1
2
3
4
print("Python", "Javascript", sep=" !! ")

결과:
Python !! Javascript

Python Javascript가 아닌 Python !! Javascript가 나오는 모습을 확인할 수 있다.

end

print()는 기본적으로 문장이 끝날때마다 줄바꿈을 한다. 2개 이상의 print()를 한 줄에 표현하려면 첫 번째 프린트의 뒤에 end=” “를 해주면 된다, end는 끝에 무엇이 오는가를 정하는 문으로

1
print("Python", "Javascript", end="?")

를 해주면 Python Javascript? 가 나오는 모습을 확인할 수 있다.

튜플 형태의 키, 값 쉽게 사용하기

튜플 형태의 자료는 키:값 형태를 띄고 있다. 이 튜플 자료를 for 문을 활용하여 키:값 의 형태로 쉽게 불러올 수 있다

1
2
3
4
5
6
7
8
9
10
scores = {"수학":100, "국어":100, "영어":90, "과학":80}

for subjects, score in scores.items(): #키:값 형태로 불러오는 items 활용
    print(subjects, score)

결과:
수학 100
국어 100    
영어 90     
과학 80  

여기서 과목과 점수사이의 거리를 늘린다거나 정렬하고싶을 때에 ljust와 rjust, 점수들을 000형태와 같이 표시하고싶다면 zfill을 활용해주면 좋다

  • ljust() ljust는 LEFT JUST를 뜻한다. 왼쪽 정렬로 값을 표시하고 ljust(num)안의 num값 만큼의 공백을 추가해준다.

  • rjust() rjust는 RIGHT JUST를 뜻한다. rjust(num)안의 num값 만큼의 공백을 추가해주고 오른쪽 정렬로 값을 표시한다.

  • zfill() zfill(num)안의 num값 만큼 0을 추가해준다. 예를 들어 score.zfill(3) 이라 하면, 수학은 그대로 100, 과학은 080의 형태로 출력된다.

표준입력

input() 을 통해 입력받는 모든 문자와 숫자들은 전부 ‘문자열’의 형태로 저장된다(type:str)

댓글남기기