Python_Day.2

조현기·5일 전

Python_Day.2

복습으로 시작해서 튜플, 딕셔너리, 집합까지 자료형을 모두 만났고,
조건문과 while 반복문으로 코드의 흐름을 제어하기 시작한 날이다.

학습 두 번째 날이다. 오늘 배운 내용은 이렇다.

  1. 복습: 문자열, 형 변환, 출력 서식
  2. 튜플과 패킹/언패킹
  3. 딕셔너리
  4. 집합
  5. 참과 거짓, 변수와 참조(id, copy, is)
  6. 조건문: if/elif/else, in/not in, pass
  7. 반복문: while, break, continue, while ~ else

1. 복습: 문자열과 형 변환

str1 = 'Python'
str2 = 'How are you?'
str3 = '''Thank you'''
str4 = """Nice to meet you"""

print(type(str1), type(str2), type(str3), type(str4))   # 전부 <class 'str'>
print(len(str1))              # 6

str5 = ''
print(type(str5), len(str5))  # <class 'str'> 0

str6 = str()                  # str5 = '' 와 같다 (list1 = list() 와 같은 원리)

int1 = int(5.6)               # 5.6을 정수형 객체로 형 변환
print(int1)                   # 5  (소수점 이하가 버려진다)

float1 = float(4)             # 4를 실수형 객체로 형 변환
print(float1)                 # 4.0

str(), int(), float(), list()처럼 클래스 이름 뒤에 괄호를 붙이면 객체를 만들거나 변환한다. 인자 없이 쓰면 빈 값이 만들어진다.

ex_str1 = 'Do you have a \"Python\"?'
print(ex_str1)                # Do you have a "Python"?

t_str1 = 'Py\tthon'           # 탭만큼 띄우기
print(t_str1)

t_str2 = 'New line \n Python'   # 줄바꿈(개행)
print(t_str2)

문자열 연산

str_1 = 'Apple'
str_2 = 'Banana'
str_4 = 'Grape'

print(3 * str_1)              # AppleAppleApple  (3번 반복)
print(str_1 + str_2)          # AppleBanana  (문자와 문자를 연결, 같은 타입끼리만 가능)

print('p' in str_1)           # True   (in: 앞의 값이 뒤의 문자열에 들어 있는가)
print('g' in str_2)           # False
print('A' in str_4)           # False  (대소문자를 구분한다)

print(type(str(33)))          # <class 'str'>
print(type(str(3.4)))         # <class 'str'>
print(type(str(True)))        # <class 'str'>   (전부 str로 변환)

in은 문자열이 포함되어 있는지를 True/False로 알려 준다. str()로는 숫자, 불린도 문자열로 바꿀 수 있다.

출력 서식 복습: 칸 수, 절삭

Day.1에서 배운 %, format을 다시 연습했다.

print('%s %s' % ('first', 'second'))
print('{}{}'.format('five', 'six'))

print('%10s' % ('python'))             # 10칸, 오른쪽 정렬
print('{:>10}'.format('python'))       # 위를 format으로 바꾼 것

print('%-10s' % 'grape')               # 왼쪽 정렬
print('{:<10}'.format('grape'))

print('{:*<10}'.format('python'))      # 10칸, 왼쪽 정렬, 빈칸은 *
print('{:*^10}'.format('python'))      # 10칸, 가운데 정렬, 빈칸은 *

절삭은 .숫자로 글자 수를 제한한다.

print('%.3s' % ('파이썬자바'))           # 파이썬
print('{:.5}'.format('파이썬자바스크립트'))   # 파이썬자바
print(f'{"파이썬자바스크립트":_>10.3}')   # _______파이썬  (3글자만 나오지만 칸은 10칸)

숫자의 칸 수는 서식 문자 앞에 숫자를 넣는다.

print('%d %d %d' % (2, 3, 4))          # 2 3 4  (%d는 정수)

print('%4d' % (12))                    # '  12'  (4칸에 정수 12)
print('{:4}'.format(12))               # 위 값을 format으로 바꾼 것

print('%f' % (3.12312389379))          # 3.123124  (6자리까지 출력, 7번째에서 반올림)

%3d처럼 쓰는 숫자는 최소 칸 수지, 최대 길이를 제한하는 것이 아니다.

print('%3d' % (12312354596990))        # 12312354596990  (3칸보다 길어도 전부 출력)
print('%3s' % ('자바바바 스크립트'))     # 전부 출력
print('%-3s' % ('그러면 이것도 될까'))   # 전부 출력
print('%3s' % ('안녕'))                 # ' 안녕'  (최소 3칸 확보, 왼쪽에 한 칸 비어서 출력)

print('%4.2f' % (3.234212345))         # '3.23'  (4칸 확보, 소수점 둘째 자리까지)
print('%5.2f' % (3.234212345))         # ' 3.23' (5칸 확보라 왼쪽에 한 칸 비어 있다)

%5.2f에서 5칸은 소수점(.)도 한 칸으로 센다. 3.23이 4글자라서 앞에 한 칸이 남는 것이다. 반대로 %4.2f는 4칸이 딱 맞아서 공백이 없다.


2. 튜플

튜플의 특징은 순서 o, 중복 o, 수정 x, 삭제 x다. 리스트와 거의 같은데 바꿀 수 없다.

tu1 = ()                  # tu1 = tuple() 과 같다
tu1 = 1,                  # 요소가 1개일 때는 쉼표(,)로 마무리해야 튜플이 된다
tu3 = (1, 2, 3, 4)
tu4 = (2, 3, '사과', 'app')
tu5 = 2, 3, ('사과', 'apple', 'grape')    # 괄호 없이도 튜플

print(type(tu5))           # <class 'tuple'>
print(tu4[1])              # 3
print(tu1[0] + tu3[3])     # 5
print(tu5[-1][2])          # grape  (-1은 뒤에서 첫 번째, 즉 마지막 요소)
# tu4[0] = 20      # 값 수정 불가, 에러 발생
# del tu4[0]       # 값 삭제 불가, 에러 발생

print(tu4[2:])             # ('사과', 'app')  (인덱스 2부터 끝까지)

괄호 ( )가 아니라 쉼표가 튜플을 만든다. tu1 = 1,은 요소 1개짜리 튜플이지만, 쉼표 없이 (1)로 쓰면 그냥 숫자 1이다.

튜플 연산과 메소드

print(tu3 + tu4)           # (1, 2, 3, 4, 2, 3, '사과', 'app')
print(tu3 * 3)             # (1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4)
print(str(tu3[0]) + 'hello')   # 1hello  (숫자를 문자열과 합치려면 str로 변환)

tu = (5, 2, 4, 4, 3)
print(tu.index(5))         # 0   (5의 위치 찾기)
print(tu.count(4))         # 2   (4의 개수 세기)

바꾸는 메소드(append, sort 같은)가 없고, 찾고 세는 index와 count만 있다.

패킹과 언패킹

  • 패킹(packing): 여러 값을 하나의 튜플, 딕셔너리, 리스트 등으로 묶는 것
  • 언패킹(unpacking): 묶여 있는 것을 개별 변수로 푸는 것
# packing
tu22 = ('phone', 'book', 'computer', 'mouse')
print(tu22)                 # ('phone', 'book', 'computer', 'mouse')

# unpacking
(x, y, z, m) = tu22         # tu22의 값을 각각 x, y, z, m에 대입
print(x, y, z, m)           # phone book computer mouse  (괄호 없이 출력)
print(type(x), type(y), type(z), type(m))   # 전부 <class 'str'>

tu33 = 1, 2, 3              # 튜플 패킹
tu44 = 1,

x1, y1, z1 = tu33           # 튜플 언패킹
x2, x3, x4 = 4, 5, 6        # 패킹과 언패킹을 한 줄에

# y2, y3 = tu44             # 에러: 변수는 2개인데 값이 1개뿐
print(x1, y1, z1)           # 1 2 3
print(x2, x3, x4)           # 4 5 6

왼쪽 변수의 개수와 오른쪽 값의 개수가 같아야 한다. 다르면 ValueError: not enough values to unpack 오류가 난다. JS의 구조 분해 할당(Day.7, Day.8)과 비슷한 역할인데, Python에서는 x, y = ...처럼 괄호 없이 바로 쓰는 것이 일반적이다.


3. 딕셔너리

딕셔너리는 {key: value} 형태이고, 특징은 순서 x, 키 중복 x, 수정 o, 삭제 o다. (참고로 요즘 Python은 넣은 순서를 기억해서 출력하지만, 리스트처럼 [0]으로 위치를 지정해서 꺼내지는 못한다. "순서가 없다"는 말은 이 뜻으로 이해하면 된다.)

dic1 = {'name': 'Lee', 'phone': '010-0000-0000', 'birth': '000211'}
dic2 = {0: '파이썬'}
dic3 = {'ary': [1, 2, 3, 4]}
dic4 = {
    'name': 'Tom',
    'addr': 'Seoul',
    'age': 25,
    'grade': 'A',
    'status': True
}
dic5 = dict()               # {}
dic6 = dict([('name', 'Tom'), ('addr', 'Seoul'), ('age', 25), ('grade', 'A'), ('status', True)])

키는 문자열뿐 아니라 숫자(0)도 될 수 있고, 값에는 리스트도 넣을 수 있다.

dict()로 만들기

dic6은 이런 순서로 만들어진다.

  1. 가장 안쪽: ('name', 'Tom') 튜플 (키, 값)
  2. 여러 튜플을 하나로 묶은 리스트
  3. dict()가 그 리스트를 딕셔너리로 변환
(a, b) 튜플 → 튜플들의 리스트 → dict() → {키: 값}
  • 튜플 안의 요소가 3개이면 에러가 난다. 반드시 키와 값 두 개로 이루어진 튜플이어야 한다.
  • 바깥 리스트 대괄호를 빼고 튜플을 나열해서 넘겨도 에러가 난다. dict()는 인자를 1개만 받는 구조이기 때문이다.

값 꺼내기

print(dic1['name'])          # Lee
# print(dic1['name1'])       # KeyError: 존재하지 않는 키

print(dic1.get('name1'))     # None  (키가 없으면 에러 없이 None)
print(dic1.get('name'))      # Lee

print(dic2[0])               # 파이썬
print(dic2.get(0))           # 파이썬

print(dic3['ary'])           # [1, 2, 3, 4]
print(dic3.get('ary'))       # [1, 2, 3, 4]

print(dic4.get('age'))       # 25
방법키가 없으면
딕셔너리명['키']KeyError 발생
딕셔너리명.get('키')None 반환 (에러 없음)

키는 하나에 값도 하나

a = {1: 'c', 1: 'a', 1: 'b', 1: 'tt'}
print(a)                     # {1: 'tt'}

같은 키를 여러 번 쓰면 마지막 값만 남는다. 키 하나에 값은 하나만 가질 수 있기 때문이다.

값 추가

dic1['address'] = 'Seoul'      # 딕셔너리명['추가할 키'] = 값
print(dic1)

dic1['score'] = [90, 30, 40]   # 값으로 리스트를 추가
print(dic1.get('score')[0])    # 90

없는 키에 값을 대입하면 추가되고, 이미 있는 키에 대입하면 수정된다.

print(len(dic1), len(dic2), len(dic3), len(dic4), len(dic5), len(dic6))
# 5 1 1 5 0 5

딕셔너리의 len()은 키:값 한 쌍을 하나로 센다.

딕셔너리 함수

print(dic1.keys())
# dict_keys(['name', 'phone', 'birth', 'address', 'score'])
print(list(dic1.keys()))     # 리스트로 변환해서 출력

print(dic1.values())
# dict_values(['Lee', '010-0000-0000', '000211', 'Seoul', [90, 30, 40]])
print(list(dic1.values()))   # 리스트로 변환

print(dic3.items())          # dict_items([('ary', [1, 2, 3, 4])])
print(list(dic3.items()))    # [('ary', [1, 2, 3, 4])]  (키와 값이 튜플로 묶여 나옴)
메소드설명
keys()키들만 모아서 반환
values()값들만 모아서 반환
items()(키, 값) 튜플들을 모아서 반환

keys()와 values()의 결과는 리스트와 비슷하게 보이지만, 리스트 고유의 함수(append, insert, pop, remove, sort)는 쓸 수 없다. 필요하면 list()로 감싸서 리스트로 바꾼다.

print(dic1.pop('name'))      # Lee  (삭제한 키의 값을 돌려준다, 원본이 변경된다)
print(dic1)                  # name이 삭제됨

print(dic4.pop('addr'))      # Seoul
print(dic4)

pop('키')는 그 키와 값을 지우고, 지운 값을 반환한다.

print('name' in dic1)        # False  (위에서 name을 pop으로 삭제했기 때문)
print('name' in dic6)        # True

딕셔너리에 in을 쓰면 키를 기준으로 존재 여부를 확인한다. (필기에는 dict.in()이라고 적었는데, in은 메소드가 아니라 연산자다.)


4. 집합

집합은 순서 x, 중복 x다.

s1 = set()                       # 빈 집합 ({}는 빈 딕셔너리)
s2 = set([1, 2, 2, 3, 4, 5])     # 리스트 자료형을 집합 자료형으로 변환

print(len(s2))                   # 5
print(s2)                        # {1, 2, 3, 4, 5}   (중복된 2가 하나로)

s3 = set([1, 4, 6, 7])
s4 = set([1, 2, '사과', '망고', 'python'])
s5 = {'phone', 'computer', 'notebook', 'water', 'phone'}   # 중복된 phone은 하나로
s6 = {12, 'mouse', (1, 2, 3), 3.14}

print(type(s1), type(s2), type(s3), type(s4), type(s5), type(s6))
# 전부 <class 'set'>

{ } 안에 키: 값 형태가 아니라 값만 나열하면 집합으로 판정된다.

다른 자료형으로 변환

print(tuple(s2), type(tuple(s2)))      # (1, 2, 3, 4, 5) <class 'tuple'>
print(tuple(s2)[0], tuple(s2)[1])      # 인덱스 사용 가능

s3_list = list(s3)
s4_list = list(s4)
print(s3_list[2], s4_list[1:3])

집합은 순서가 없어서 인덱스를 쓸 수 없지만, 튜플이나 리스트로 바꾸면 인덱스를 쓸 수 있다. 다만 set으로 선언했을 때 순서가 유지되지 않아서, 특히 문자열이 섞인 집합은 실행할 때마다 s4_list[1:3]의 결과가 달라질 수 있다.

집합 연산

set1 = set([1, 2, 3, 4, 5, 6])
set2 = set([4, 5, 6, 7, 8, 9])

# 교집합: 둘 다 가진 값
print(set1 & set2)                  # {4, 5, 6}
print(set1.intersection(set2))

# 합집합: 중복은 하나로, 두 집합의 요소를 전부
print(set1 | set2)                  # {1, 2, 3, 4, 5, 6, 7, 8, 9}
print(set1.union(set2))

# 차집합: set1의 요소 중 set2와 겹치는 것을 뺀 나머지
print(set1 - set2)                  # {1, 2, 3}
print(set1.difference(set2))
연산기호메소드의미
교집합&intersection()공통 요소
합집합\|union()모든 요소 (중복은 하나로)
차집합-difference()앞 집합에서 뒤 집합과 겹치는 것을 뺌
# 두 집합에 공통 요소가 없으면 True, 있으면 False
print(set1.isdisjoint(set2))        # False  (4, 5, 6이 공통)

# set1이 set2의 부분집합인가? (set1의 모든 요소가 set2에 있는가)
print(set1.issubset(set2))          # False
# 만약 set2가 {1, 2, ..., 9}였다면 True

# set1이 set2의 모든 요소를 포함하는가? (위와 같은 의미, 순서만 반대)
print(set1.issuperset(set2))        # False  (set1에 7, 8, 9가 없으므로)

추가, 삭제

a = set([1, 2, 3, 4])               # {1, 2, 3, 4}

a.add(5)                            # 하나의 값을 추가 (원본 변경)
print(a)                            # {1, 2, 3, 4, 5}

a.remove(2)                         # 값 2를 삭제
print(a)                            # {1, 3, 4, 5}

a.discard(3)                        # 값 3을 삭제
print(a)                            # {1, 4, 5}

a.clear()                           # 전부 제거 (구조는 남는다)
print(a)                            # set()

a.update([1, 2])                    # 여러 값을 한꺼번에 추가
print(a)                            # {1, 2}

remove와 discard는 지우는 일은 같지만 없는 값을 지울 때 다르다.

메소드없는 값을 지우면
remove(값)KeyError 발생
discard(값)아무 일도 없이 넘어감

remove는 인자를 하나만 받아서 remove(2, 3)처럼 쓰면 에러가 난다. 여러 값을 한 번에 넣을 때는 update를 쓰고, 이때 update는 리스트처럼 순회할 수 있는 것을 받는다. 집합은 중복이 안 되어서 같은 값은 하나만 들어가고, 순서도 유지되지 않는다.


5. 자료형 정리

자료형기호순서중복수정
리스트[ ]ooo
튜플( )oox
딕셔너리{ : }x키 xo
집합{ }xxo

각 타입의 특징을 이해하고 상황에 맞게 골라 써야 한다.

자료형의 참과 거짓

  • 거짓으로 구분되는 것: 값이 없음(None), 비어 있음({}, [], (), ''), 0, False
  • 참으로 구분되는 것: 값이 있는 경우
참:   '자바스크립트', ['파이썬'], (10), {'a': 3}, 1, {'aa'}
거짓: '', "", [], (), {}, 0, None

필기의 거짓 목록에 Null이 같이 적혀 있었는데, Python에는 Null이 없고 "값 없음"은 None이다. JS의 null에 해당한다. 그리고 JS에서는 빈 배열 []이 참이었지만(Day.9에서 정리한 falsy 목록에 없다), Python에서는 빈 리스트가 거짓이다. 같은 모양인데 규칙이 다르다.


6. 변수와 참조

# a = [1, 2, 3]
# a는 리스트 [1, 2, 3]이 저장된 메모리 주소값을 가지고 있다 (reference)

n = 100
print(type(n))        # <class 'int'>
print(id(n))          # n이 가리키는 객체의 주소값

a = b = c = d = 100   # 동시에 여러 변수 선언 가능
n = 50                # 값을 덮어쓴다
print(n)              # 50

id()는 객체의 주소값을 알려 준다. 변수는 값을 직접 품고 있는 상자가 아니라, 값(객체)이 있는 곳을 가리키는 이름표다.

a = 3
print(id(3))
print(id(a))
b = a
print(id(b))          # 세 개 모두 같은 값

a와 b는 같은 객체 3을 가리키고 있어서 id가 같다. 이때 b = a는 값을 복사하는 것이 아니라 같은 곳을 가리키게 하는 것이다.

두 변수의 값 바꾸기

x = 3
y = 5
x, y = (y, x)         # y의 값을 x에, x의 값을 y에 대입
print(x, y)           # 5 3

임시 변수 없이도 한 줄로 값을 맞바꿀 수 있다. 오른쪽에서 패킹이 일어나고 왼쪽에서 언패킹이 일어나는 구조다.

복사: copy

from copy import copy        # from 모듈명 import 함수

a2 = [1, 2, 3]
b2 = copy(a2)                # a2를 복사한 새로운 객체가 생성된다
# b2 = copy(a2) 는 b2 = a2[:] 와 같다

print(a2, b2)                # [1, 2, 3] [1, 2, 3]  (값은 같지만)
print(id(a2), id(b2))        # id가 다르다
print(a2 is b2)              # False

is는 두 대상이 같은 객체인지(주소가 같은지) 확인한다. 값이 같은지를 묻는 ==와는 다르다. 위에서 a2 == b2는 True지만 a2 is b2는 False다. JS에서 new String('hi') 두 개를 ===로 비교하면 false였던 Day.5의 내용과 같은 문제다. 또 Day.8에서 본 스프레드 복사([...ary])에 해당하는 것이 Python의 copy()와 슬라이싱 [:]이다.


7. 조건문

기본 구조

print(type(True))     # <class 'bool'>
print(type(False))

if True:              # True를 조건으로 두면 무조건 참이라 항상 실행된다
    print('참')
else:
    print('거짓')

JS와 달리 조건에 소괄호가 필요 없고, 조건 뒤에 콜론(:), 실행할 코드는 들여쓰기로 구분한다. 중괄호가 없는 대신 들여쓰기가 곧 문법이다.

참/거짓 판정 활용

adr = ''              # adr = False 와 같다
if adr:
    print(adr)
else:
    print('주소를 입력하세요')      # 이 줄이 실행됨

adr = 'Seoul'         # adr = True 와 같다
if adr:
    print(adr)        # Seoul
else:
    print('주소를 입력하세요')

빈 문자열은 거짓이라 else로, 값이 있으면 참이라 if로 간다.

논리 연산자

JS의 &&, ||, !가 Python에서는 and, or, not이다.

a, b, c = 85, 29, 20
print(a > b and b > c)       # True   (둘 다 맞으면 True)
print(a > b or b > c)        # True   (둘 중 하나라도 맞으면 True)
print(not a > b)             # False  (a가 b보다 크지 않으면 True)
print(not b > c)             # False
print(not True)              # False
print(not False)             # True

print(3 + 2 > 7 + 3)                    # False
print(3 + 2 == 5 and not 7 + 3 > 0)     # False  (True and False → and는 둘 다 True여야 True)
JavaScriptPython
&&and
\|\|or
!not

in, not in

x = [10, 20, 30]                      # list
y = {70, 80, 90, 100}                 # set
z = {'name': '파이썬', 'city': '서울', 'id': 'academy'}   # dict
m = (10, 20, 24)                      # tuple

print(25 in x)             # False  (25가 x 안에 있는가)
print(25 not in x)         # True   (25가 x 안에 없는가)
print(90 in y)             # True
print(20 not in m)         # False

print('city' in z)         # True   (city라는 키가 z 안에 있는가)
print('academy' in z.values())   # True

딕셔너리에 in을 직접 쓰면 키만 검사한다. 값에서 찾으려면 z.values()와 함께 쓴다.

elif, pass, 중첩 조건문

score = 82

if score >= 90:
    print('A')
elif score >= 80:
    print('B')            # 이 줄이 실행됨
elif score >= 70:
    print('C')
else:
    print('F')

JS의 else if가 Python에서는 elif다.

pass는 아무 일도 하지 않고 넘어가는 문장이다. 문법상 코드 블록이 있어야 하는데 할 일이 없을 때 쓴다.

grade = 'A'
total = 85

if grade == 'A':
    if total >= 90:
        print('장학생')
    elif total >= 80:
        pass              # 아무것도 하지 않고 넘어감 (출력 없음)
    else:                 # total이 80 미만일 때
        print('학생')
else:                     # grade가 A가 아닐 때
    print('다시 시험을 봐야 한다')

grade가 'A'이고 total이 85이므로 elif total >= 80 쪽으로 들어가 pass가 실행되고, 출력은 아무것도 없다.


8. 반복문: while

기본 구조

# 초기식, 조건, 증감
# while 조건:
#     반복할 실행 코드

num = 5
while num > 0:
    print(num)            # 5 4 3 2 1
    num -= 1              # num = num - 1

JavaScript Day.4에서 정리한 초기식, 조건식, 증감식 세 가지가 while에서도 그대로 필요하다. 증감식을 빼면 무한 반복이 된다.

a = ['a', 'b', 'c']
while a:                  # 리스트에 값이 있으면 참, 비면 거짓
    print(a.pop())        # c b a

리스트가 빌 때까지 pop()으로 꺼내며 출력한다. 앞에서 정리한 참/거짓 규칙(빈 리스트는 거짓)을 조건으로 쓴 것이다. while True:로 쓰면 무한 반복문이 된다.

break와 continue

  • break: 반복문을 완전히 빠져나간다.
  • continue: 이번 반복의 남은 부분을 건너뛰고 처음(조건 검사)으로 돌아간다.
num = 10
while num > 0:
    print(num)            # 10 9 8 7 6 5 4 3
    num -= 1
    if num == 2:
        break             # num이 2가 되면 반복문 종료
n = 10
while n > 0:
    n -= 1
    if n == 2:
        continue          # 2일 때만 건너뛰고 다음 반복으로
    print(n)              # 9 8 7 6 5 4 3 1 0  (2만 빠진다)

필기의 주석에는 continue가 "반복문 탈출"이라고 적혀 있었는데, 탈출은 break다. continue는 탈출하지 않고 그 회차만 건너뛴다. 위 결과에서 2만 빠지고 1, 0까지 계속 출력되는 것이 증거다.

continue 뒤에 print가 하나 더 있는 경우도 확인했다.

num = 10
while num > 0:
    print(num)
    num -= 1
    if num == 2:
        continue          # num이 2일 때는 아래 print를 건너뜀
    print(num)
# 10 9 9 8 8 7 7 6 6 5 5 4 4 3 3 2 1 1 0

한 회차에 print가 두 번 있어서 대부분의 값이 두 번씩 나온다. num이 2가 된 회차에서만 두 번째 print가 빠져서, 3이 두 번 나온 뒤 2가 한 번만 나온다.

i = 1
while i <= 10:
    print(i)              # 1 2 3 4 5
    if i == 5:
        break
    i += 1

while과 else

n = 10
while n > 0:
    n -= 1
    print(n)
    if n == 5:
        break
    else:
        print('final')
# 9 final 8 final 7 final 6 final 5

이 else는 while이 아니라 안쪽 if의 else다. 5에서 break가 되기 전까지 final이 반복해서 찍힌다.

while에도 else를 붙일 수 있다.

a1 = ['water', 'python', 'java', 'phone']
s1 = 'py'
i = 0

# 리스트 끝까지 'py'와 같은 값이 있는지 확인, 있으면 반복문 종료
while i < len(a1):
    if a1[i] == s1:
        break
    i += 1
else:
    print('hi')           # hi

while ~ else의 else는 반복문이 break 없이 정상적으로 끝났을 때만 실행된다. 중간에 break로 끝나면 else는 실행되지 않는다. 위 코드는 'py'와 정확히 같은 요소가 없어서 끝까지 돌았고('python'은 'py'와 같지 않다), 그래서 hi가 출력된다. "끝까지 찾아봤는데 없었다"를 표현할 때 쓸 수 있다.

무한 반복과 탈출

li = ['a', 'b', 'c']
while True:               # 무한 루프 안에서
    if not li:            # 리스트가 비면 not li가 True가 되어
        break             # 반복문 종료
    print(li.pop())       # c b a

값이 있을 때는 not li가 False라 break가 작동하지 않고, pop()으로 값이 다 사라지면 not li가 True가 되어 반복문이 끝난다. while li:로 쓴 앞의 코드와 같은 일을 하는 다른 모양이다.


9. 퀴즈 오답 정리: 집합 만들기

문제: [1, 2, 3]을 집합 자료형으로 바꾼다.

n_set = ([1, 2, 3])       # 오답
print(n_set)              # [1, 2, 3]  (집합이 아니라 괄호로 감싼 리스트)

정답은 set()으로 변환하는 것이다.

n_set = set([1, 2, 3])
print(n_set)              # {1, 2, 3}
  • ([1, 2, 3])의 괄호는 튜플이 아니라 그냥 묶음 괄호라서, 결과는 여전히 리스트다.
  • 집합이 { } 모양이라고 해서 {[1, 2, 3]}으로 쓰면 에러가 난다. 리스트는 바뀔 수 있는 값이라 집합의 요소나 딕셔너리의 키가 될 수 없다(TypeError: unhashable type: 'list'). 집합에는 숫자, 문자열, 튜플처럼 바뀌지 않는 값만 들어간다.
  • 리스트를 집합으로 만들려면 set(리스트)로 변환해야 한다.

마무리

  • 튜플은 수정할 수 없는 리스트이고, 요소가 하나면 쉼표로 끝내야 한다.
  • 딕셔너리는 키로 값을 찾고, 없는 키를 [ ]로 꺼내면 KeyError가 나지만 get()은 None을 돌려준다.
  • 집합은 중복이 없고 순서도 없으며, &, |, -로 교집합, 합집합, 차집합을 구한다.
  • 빈 값(None, '', [], (), {}, 0)은 거짓이다.
  • ==는 값이 같은지, is는 같은 객체인지를 비교한다.
  • 조건문은 콜론과 들여쓰기로 쓰고, else if는 elif, &&/||/!는 and/or/not이다.
  • while ~ else의 else는 break 없이 끝났을 때만 실행된다.

Tags: Python 튜플 딕셔너리 집합 조건문 while 반복문 개발자 학습기록

profile
난 뭘까

0개의 댓글