파이썬 2는 2020년 1월 1일 수명이 다함
더 이상 버그 수정, 보안 패치, 새로운 기능의 역포팅이 이뤄지지 않는다.
따라서 파이썬 3를 사용해야 한다.
파이썬에는 문자열 데이터의 시퀀스를 표현하는 두 가지 타입(bytes와 str)이 있다.
아래 코드처럼 bytes 타입의 인스턴스에는 부호가 없는 8바이트 데이터가 그대로 들어간다.(종종 아스키 인코딩을 사용해 내부 문자를 표시한다.)
a = b'h\x65llo'
print(list(a)) # [104, 101, 108, 108, 111]
print(a) # b'hello'
str 인스턴스에는 사람이 사용하는 언어의 문자를 표현하는 유니코드 코드 포인트(code point)가 들어 있다.
a = 'a\u0300 propos'
print(list(a)) # ['a', '`', ' ', 'p', 'r', 'o', 'p', 'o', 's']
print(a) # à propos
유니코드 샌드위치를 사용! - 유니코드 데이터를 인코딩하거나 디코딩하는 부분을 인터세이흐의 가장 먼 경계지점에 위치시키는 방식
문자를 표현하는 타입이 둘로 나뉘어 있기 때문에 파이썬 코드에서는 다음과 같은 두 가지 상황이 자주 발생
두 경우를 변환해주고 입력 값이 코드가 원하는 값과 일치하는지 확신하기 위해 종종 두 가지 도우미 함수가 필요하다.
첫 번째 함수는 bytes나 str 인스턴스를 받아서 항상 str을 반환
def to_str(bytes_or_str):
if isinstance(bytes_or_str, bytes):
value = bytes_or_str.decode('utf-8')
else:
value = bytes_or_str
return value
print(repr(to_str(b'foo')))
print(repr(to_str('bar')))
print(repr(to_str(b'\xed\x95\x9c'))) # UTF-8에서 한글은 3바이트임
>>>
'foo'
'bar'
'한'
두 번째 함수는 bytes나 str 인스턴스를 받아서 항상 bytes를 반환한다.
def to_bytes(bytes_or_str):
if isinstance(bytes_or_str, str):
value = bytes_or_str.encode('utf-8')
else:
value = bytes_or_str
return value
print(repr(to_bytes(b'foo')))
print(repr(to_bytes('bar')))
print(repr(to_bytes('한글')))
>>>
b'foo'
b'bar'
b'\xed\x95\x9c\xea\xb8\x80'
+
연산자를 사용하면 bytes를 bytes에 더하거나 str을 str에 더할 수 있다.형식화(formatting)는 미리 정의된 문자열에 데이터 값을 끼워 넣어서 사람이 보기 좋은 문자열로 저장하는 과정이다.
- 파이썬 문법을 사용하면 아주 복잡하고 읽기 어려운 한 줄짜리 식을 쉽게 작성할 수 있다.
- 식이 복잡해지기 시작하면 바로 식을 더 작은 조각으로 나눠서 로직을 도우미 함수로 옮겨야 한다.
- 특히 같은 로직을 반복해 사용할 때는 도우미 함수를 꼭 사용하라.
- 불(boolean) 연산자 or나 and를 식에 사용하는 것보다 if/else 식을 쓰는 편이 더 가독성이 좋다.
my_values = {'빨강': ['5'], '파랑': ['0'], '초록': ['']}
# 1
print('빨강:', my_values.get('빨강'))
print('초록:', my_values.get('초록'))
print('투명도:', my_values.get('투명도'))
빨강: ['5']
초록: ['']
투명도: None
# 2
red = my_values.get('빨강', [''])[0] or 0
green = my_values.get('초록', [''])[0] or 0
opacity = my_values.get('투명도', [''])[0] or 0
print(f'빨강: {red')
print(f'초록: {green}')
print(f'투명도: {opacity}')
빨강: '5'
초록: 0
투명도: 0
# 3
red = int(my_values.get('빨강', [''])[0] or 0)
# 4
red_str = my_values.get('빨강', [''])
red = int(red_str[0]) if red_str[0] else 0
# 5
green_str = my_values.get('초록', [''])
if green_str[0]:
green = int(green_str[0])
else:
green = 0
# 6
def get_first_int(values, key, default=0):
found = values.get(key, [''])
if found[0]:
return int(found[0])
return default
green = get_first_int(my_values, '초록')
# 1
item = ('호박엿', '식혜')
first, second = item # 언패킹
print(first, '&', second)
>>>
호박엿 & 식혜
# 2 - 언패킹 구문 미사용
def bubble_sort(a):
for _ in range(len(a)):
for i in range(1, len(a)):
if a[i] < a[i-1]:
temp = a[i]
a[i] = a[i-1]
a[i-1] = temp
names = ['프레즐', '당근', '쑥갓', '베이컨']
bubble_sort(names)
print(name)
>>>
['당근', '베이컨', '쑥갓', '프레즐']
# 3 - 언패킹 구문 사용
def bubble_sort(a):
for _ in range(len(a)):
for i in range(1, len(a)):
if a[i] < a[i-1]:
a[i-1], a[i] = a[i], a[i-1]
names = ['프레즐', '당근', '쑥갓', '베이컨']
bubble_sort(names)
print(name)
# 4- for 루프 또는 그와 비슷한 다른 요소(컴프리헨션이나 제너레이터 식)의 대상인 리스트의 원소를 언패킹
snacks = [('베이컨', 350), ('도넛', 240), '머핀'190)
for i in range(len(snacks)):
item = snacks[i]
name = item[0]
calories = item[1]
print(f'#{i+1}: {name} 은 {calories} 칼로리입니다.')
>>>
#1: 베이컨 은 350 칼로리입니다.
#2: 도넛 은 240 칼로리입니다.
#3: 머핀 은 190 칼로리입니다.
# 5
for rank, (name, calories) in enumerate(snacks, 1):
print(f'#{rank}: {name} 은 {calories} 칼로리입니다.')
>>>
#1: 베이컨 은 350 칼로리입니다.
#2: 도넛 은 240 칼로리입니다.
#3: 머핀 은 190 칼로리입니다.
# 1 - range 내장 함수
from random import randint
random_bits = 0
for i in range(32):
if randint(0, 1):
random_bits |= << i
print(bin(random_bits))
0b11101000100100000111000010000001
# 2 - 문자열로 이뤄진 list
flavor_list = ['바닐라', '초콜릿', '피칸', '딸기']
for flavor in flavor_list:
print(f'{flavor} 맛있어요.')
바닐라 맛있어요.
초콜릿 맛있어요.
피칸 맛있어요.
딸기 맛있어요.
# 3 - 몇 번째 순서인지 알고 싶을 때
for i in range(len(flavor_list)):
flavor = flavor_list[i]
print(f'{i + 1}: {flavor}')
1: 바닐라
2: 초콜릿
3: 피칸
4: 딸기
# 4 - enumerate 사용
it = enumerate(flavor_list)
print(next(it))
print(next(it))
(0, '바닐라')
(1, '초콜릿')
# 5 - for 문에서 간결하게 언패킹하여 사용
for i, flavor in enumerate(flavor_list):
print(f'{i + 1}: {flavor}')
1: 바닐라
2: 초콜릿
3: 피칸
4: 딸기
# 6 - 시작 지정
for i, flavor in enumerate(flavor_list, 1):
print(f'{i}: {flavor}')
1: 바닐라
2: 초콜릿
3: 피칸
4: 딸기
# 1 - 리스트 컴프리헨션을 사용하면 소스 list에서 새로운 list를 파생시키기 쉽다
names = ['Cecila', '남궁민수', '현우']
counts = [len(n) for n in names]
print(counts)
>>>
[7, 4, 3]
# 2 - 만들어진 list의 각 원소는 소스 list에서 같은 인덱스 위치에 있는 원소와 관련이 있다. 두 리스트를 동시에 이터레이션할 경우 names 소스 리스트의 길이를 사용해 이터레이션 할 수 있다.
longest_name = None
max_count = 0
for i in range(len(names)):
count = counts[i]
if count > max_count:
longest_name = names[i]
max_count = count
print(longest_name)
>>>
Cecilia
# 문제는 이 루프가 시각적으로 잡음이 많다
# 3 - enumerate를 사용하면 약간 나아지지만, 이 코드도 여전히 이상적이지는 않다
for i, name in enumerate(names):
count = counts[i]
if count > max_count:
longest_name = name
max_count = count
# 4 - zip 내장 함수 사용
for name, count in zip(names, counts):
if count > max_count:
longest_name = name
max_count = count
# 5 - 이터레이터의 길이가 다른 경우
names.append('Rosalind')
for name, count in zip(names, counts):
print(name)
>>>
Cecila
남궁민수
현우
# 6 itertools의 zip_longest 사용
for name, count in itertools.zip_longest(names, counts):
print(f'{name}: {count}')
>>>
Cecilia: 7
남궁민수: 4
현우: 2
Rosalind: None
# 1 - 파이썬에서는 루프가 반복수행하는 내부 블록 바로 다음에 else 블록을 추가할 수 있다.
for i in range(3):
print('Loop', i)
else:
print('Else block')
>>>
Loop 0
Loop 1
Loop 2
Else block!
# 여기서 else는 '처리할 예외가 없는 경우에 이 블록을 실행하라'는 뜻
# 2 - 실제로 루프 안에서 break 문을 사용하면 else 블록이 실행되지 않는다
for i in range(3):
print('Loop', i)
if i == 1:
break
else:
print('Else block')
>>>
Loop 0
Loop 1
# 3 - 빈 시퀀스에 대한 루프를 실행하면 else 블록이 바로 실행됨
for x in []:
print('이 줄은 실행되지 않음')
else:
print('For Else block!')
>>>
For Else block!
# 4 - while 루프의 조건이 처음부터 False인 경우(루프가 한 번도 실행되지 못하는 경우)에도 else 블록이 바로 실행됨
while False:
print('이 줄은 실행되지 않음')
else:
print('While Else block!')
>>>
While Else block!
# 5 - 서로소를 찾는 동작
a = 4
b = 9
for i in range(2, min(a, b) + 1):
print('검사 중', i)
if a % i == 0 and b % i ==0:
print('서로소 아님')
break
else:
print('서로소')
>>>
검사 중 2
검사 중 3
검사 중 4
서로소
# 6 - 차라리 도우미 함수를 작성하는 것이 좋음
# 6-1 원하는 조건을 찾자마자 빠르게 함수를 반환하는 방식
def coprime(a, b):
for i in range(2, min(a,b) + 1):
if a % i == 0 and b % i == 0:
return False
return True
assert coprime(4, 9)
assert not coprime(3, 6)
# 6-2 원하는 대상을 찾았는지 나타내는 결과 변수를 도입
def coprime_alternate(a, b):
is_coprime = True
for i in range(2, min(a, b) + 1):
if a % i == 0 and b % i == 0:
is_coprime = False
break
return is_coprime
assert coprime_alternate(4, 9)
assert not coprime_alternate(3, 6)
# 1 - 필요 이상으로 잡음이 많다
fresh_fruit = {
'사과': 10,
'바나나': 8,
'레몬': 5
}
def make_lemonade(count):
...
def out_of_stock():
...
count = fresh_fruit.get('레몬', 0)
if count:
make_lemonade(count)
else:
out_of_stock()
# 2 - 대입식을 사용하였을 때
if count := fresh_fruit.get('레몬', 0):
make_lemonade(count)
else:
out_of_stock()
# 3 - count 대입이 변수를 쓸데없이 너무 강조
def make_cider(count):
...
count = fresh_fruit.get('사과', 0)
if count >= 4:
make_cider(count)
else:
out_of_stock()
# 4 - 왈러스 연산자 사용
if (count := fresh_fruit.get('사과', 0)) >= 4:
make_cider(count)
else:
out_of_stock()