오늘 배운 내용도 마찬가지로 concept은 모두 아는 내용이었다. 하지만 역시 여러모로 미세하게 다른 부분들이 많았다. 파이썬에만 존재하는 함수들이 여럿 있었으며 이런 함수들은 다소 생소했다. 자료구조의 경우 같은 컨셉 구현을 위해 여러 다른 자료구조를 사용할 수도 있었고 다소 생소한 자료구조도 한 두개 있었던 것 같다. 파이썬다운 코드를 짜고, 파이썬다운 코드를 이해하기 위해서는 위에 언급한 파이썬만의 고유한 것들을 알아야한다. 기존 C/C++ 스타일로 코드를 짜면 파이썬을 사용하는 의미가 무색해질 것이다.
오늘은 아래 2가지 주제를 다루었는데, 각자에서 배운 내용이 상당히 많았다.
스택, 큐, 튜플, 셋, 딕셔너리, 컬렉션(모듈)에 대하여 다루었다.
append()
, pop()
함수를 사용하여 단순 list로도 구현 가능하다.pop()
의 경우 맨 처음 들어온 인자를 제거해야하므로 pop(0)
과 같이 pop
함수의 파라미터를 0으로 줘야한다.const
로 선언된 배열과 비슷하다고 보면 될 것 같다. 따라서 튜플은 변경되지 않는 값들을 주고 받을 때 사용자의 실수를 사전에 방지할 수 있다.t = (1, 2, 3)
) 메소드도 리스트에 있는 것과 거의 같다.t = (1)
과 같이 쓰는 것은 일반적인 연산시 괄호를 붙이는 것으로 인식되므로 원소가 한 개인 튜플 선언이 필요할 시 t = (1, )
와 같이 사용해야한다.s = {1, 2, 3}
#set_example.py
s1 = {1, 2, 3} # s = set([1, 2, 3])으로도 선언 가능하다.
s2 = {2, 3, 5}
s1.intersection(s2) #{2, 3}
s1.union(s2) #{1, 2, 3, 5}
s1.difference(s2) #{1}
intersection
,union
,difference
함수 대신&
,|
,-
연산자를 사용해도 된다. i.e.s1 & s2
remove
, update
, discard
, clear
등의 메소드가 존재한다.remove
는 존재하지 않는 원소를 지우려하면 에러가 발생하지만 discard
는 같은 상황에서 에러가 발생하지 않는다는 차이가 있다. #remove_duplicated.py
mylist = [1, 1, 3, 4, 5, 5]
mylist = list(set(mylist))
print(mylist) #[1, 3, 4, 5]
for
문을 돌리면 tuple 형태로 key-value 쌍이 나오게 된다. #dictionary_example.py
dic = {1: "car", 2: "train", 3: "bus", 4: "airplane"}
dic[2] = "walk"
for k, v in dic.items(): #key는 keys(), value는 values(), 둘다는 items()
print(k, v)
자바에서의 컬렉션과 비슷한 것 같다. list, tuple, dict에 대한 python built-in 확장 자료구조(모듈)이다. collections를 import해서 사용한다.
#import_deque.py
from collections import deque
collections에는 많은 클래스가 있는데, 일단은 deque, defaultdict, counter, namedtuple 에 대해서 먼저 알고 가기로 한다.
#deque.py
from collections import deque
deque_list = deque()
for i in range(5):
deque_list.append(i)
deque_list.appendleft(10) #deque([10, 0, 1, 2, 3, 4])
append
, appendleft
, extend
, extendleft
, pop
, popleft
, rotate
등의 메소드가 존재한다.rotate
의 경우 iterate 연산시의 시작 원소의 위치를 바꾸게 된다. 양의 방향이 오른쪽 방향이다.다만 defaultdict는 선언시 초기값 지정이 필요하다. 자료형을 인자로 넣으면 해당 자료형의 default value가 들어가며, 그 외 직접 지정하고싶으면 lambda
를 이용하면 된다.
#defaultdict.py
from collections import defaultdict
d_dic1 = defaultdict(int) # d = defaultdict(object)가 기본 선언 형태
print(d_dic1["a"]) #0
d_dic2 = defaultdict(lambda: 5)
print(d_dic2["b"]) #5
#counter.py
from collections import Counter
counter = Counter('hello world')
print(counter)
#Counter({'l': 3, 'o': 2, 'h': 1, 'e': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1})
print(counter.most_common(n=2))
#[('l', 3), ('o', 2)]
union
, intersection
연산 등도 가능하다. #namedtuple.py
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(5, y = 3)
print(p) #Point(x=5, y=3)
print(p[0] + [1]) #8
x, y = p
print(p.x + p.y) #8
print(x) #5
파이썬다운 코드를 말한다. Pythonic한 코드를 짤수록 대체로 속도가 빠르다. 또한 코드 자체가 간결하여 가독성도 좋아진다. 파이썬에 익숙하지 않기 때문에 앞으로 특히 Pythonic한 코드를 짜기 위해 신경써야 할 것 같다.
어제 과제를 진행하면서 split
, join
함수를 적극적으로 사용해야겠다고 생각했는데 역시 이 파트에서도 언급되었다.
split
함수는 파라미터로 들어온 문자를 기준으로 주어진 문자열을 잘라 리스트로 만드는 함수이다. join
함수는 반대로 파라미터로 들어온 문자를 기준으로 주어진 리스트를 문자열로 합치는 함수이다.
#split_and_join.py
a = "I am groot"
b = a.split() #parameter를 안주면 공백을 기준으로 자른다.
print(b) #['I', 'am', 'groot']
c = ' '.join(b)
print(c) #I am groot
피어세션때 알게된건데, split에 인자를 주지 않은 상태, 즉 공백으로 자르게되면 연속되는 공백은 모두 무시하게 된다. 그런데 parameter로 특정 문자를 주게 되면 그 특정 문자가 연속할 때 모두 공백으로 처리되어 리스트에 들어가게 된다.
#split_more.py
a = "___EXAMPLE___WORD___"
b = " EXTRA SPACE "
print(a.split('_'))
#['', '', '', 'EXAMPLE', '', '', 'WORD', '', '', '']
print(b.split())
#['EXTRA', 'SPACE']
list comprehension은 딱히 번역이 없어 영어로 많이 쓴다고 한다.
리스트를 즉각적으로 생성해야 할 때 사용되며 속도도 더 빠르다고.
List comprehension시 중첩 for
문도 사용할 수 있으며 if
문을 통해 filtering도 즉각적으로 가능하므로 아주 강력한 도구인 것 같다.
안쪽 for
문을 괄호로 감싸줌으로써 2차원 리스트도 만들 수 있다.
#list_comprehension.py
result = [i for i in range(10)]
print(result) #[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
word_1 = "abc"
word_2 = "abc"
result = [i + j for i in word_1 for j in word_2] #Nested For
print(result)
#['aa', 'ab', 'ac', 'ba', 'bb', 'bc', 'ca', 'cb', 'cc']
result = [i + j if not(i == j) else "X" for i in word_1 for j in word_2] #Filter
print(result)
#['X', 'ab', 'ac', 'ba', 'X', 'bc', 'ca', 'cb', 'X']
case_1 = ["A", "B", "C"]
case_2 = ["D", "E", "A"]
result = [i + j for i in case_1 for j in case_2]
print(result)
#['AD', 'AE', 'AA', 'BD', 'BE', 'BA', 'CD', 'CE', 'CA']
result = [[i + j for i in case_1] for j in case_2]
print(result)
#[['AD', 'BD', 'CD'], ['AE', 'BE', 'CE'], ['AA', 'BA', 'CA']]
python에서는 대부분의 for
문에 index 값을 사용하지 않기 때문에 index를 다루면서 무언가를 하기가 쉽지 않았다. enumerate를 사용하면 index 값을 지정할 수 있다.
#enumerate.py
alphabet = ['a', 'b', 'c', 'd']
#여기서 나오는 (i, v)쌍은 기본적으로 tuple 구조이다.
for i, v in enumerate(alphabet):
print(i, v)
#0 a
#1 b
#2 c
#3 d
#아래와 같이 바로 딕셔너리를 만들 수도 있다.
dic = {v : i for i, v in enumerate(a)} #v, i의 순서를 바꿀 수도 있다.
print(dic) #{'a': 0, 'b': 1, 'c': 2, 'd': 3}
zip은 두 list를 병렬적으로 추출하는 데에 활용된다. 이것도 아래 코드를 보면 바로 이해가 된다.
#zip.py
alist = ["a1", "a2", "a3"]
blist = ["b1", "b2", "b3"]
[ [a, b] for a, b in zip(alist, blist) ]
#[['a1', 'b1'], ['a2', 'b2'], ['a3', 'b3']]
[ c for c in zip(alist, blist) ] #zip은 기본적으로 tuple 구조를 뱉는다.
#[('a1', 'b1'), ('a2', 'b2'), ('a3', 'b3')]
list(enumerate(zip(alist, blist))) #enumerate와 함께 활용
#아래 결과값을 보면 enumerate도 tuple 구조를 뱉는다는 것을 다시 확인할 수 있다.
#[(0, ('a1', 'b1')), (1, ('a2', 'b2')), (2, ('a3', 'b3'))]
#lambda.py
f = (lambda x, y : x + y)
print(f(10, 50)) #60
#map.py
target = [1, 2, 3, 4, 5]
#lambda 함수를 활용한 매핑
result = map(lambda x : x ** 2, target)
#list로의 타입캐스팅이 필요
list(result) # [1, 4, 9, 16, 25]
def f(x):
return x + 5
list(map(f, target)) # [6, 7, 8, 9, 10]
def f2(x, y):
return x + y
list(map(f2, target, target)) # [2, 4, 6, 8, 10]
#reduce.py
from functools import reduce
print(reduce(lambda x, y: x + y, [1,2,3,4,5])) # 15
sequence 자료형에서 데이터를 차례대로 가리킬 수 있는 객체이다. (반복 가능한 객체)
loop이 돌 때마다 매번 iterator는 다음 원소를 가리킨다.
#iterator.py
cities = ["seoul", "busan", "jeju"]
memory_address_cities = iter(cities)
next(memory_address_cities) #'seoul'
next(memory_address_cities) #'busan'
next(memory_address_cities) #'jeju'
next(memory_address_cities) #ERROR!
yield
키워드를 사용하여 loop이 돌 때마다 데이터를 올려준다.즉, 필요할 때마다 불러와서 쓰기 때문에 iterating하는 데이터가 많아져도(무한한 순서가 있어도) 메모리 사용량이 커지지 않는다.
#generator_1.py
def test_generator():
yield 1
yield 2
yield 3
gen = test_generator()
type(gen) #<class 'generator'>
next(gen) #1
next(gen) #2
next(gen) #3
next(gen) #StopIteration (ERROR)
아래 코드를 보면 generator에 대한 느낌을 확 받을 수 있다. 내부의 변수는 유지되며 필요할 때마다 generator 내부의 iterator가 돌면서 하나씩 값을 받아올 수 있다.
#generator_2.py
def infinite_generator():
count = 0
while True:
count += 1
yield count
gen = infinite_generator()
next(gen) #1
next(gen) #2
...
next(gen) #13
...
python 3.3부터는 yield from
을 사용할 수 있게 되면서 아래와 같은 코드 작성이 가능하다.
#generator_3.py
def three_generator():
a = [1, 2, 3]
for i in a:
yield i
#위 함수를 python 3.3 이상부터는 아래와 같이 작성 가능하다.
def three_generator():
a = [1, 2, 3]
yield from a
list comprehension의 형태로 generator list를 생성할 수 있다. 이 때, 대괄호 대신 소괄호를 사용한다.
#generator_4.py
a = (n * n for n in range(500))
next(a) #0
next(a) #1
next(a) #4
...
#keyword_argument.py
def get_rectangle_area(width, height):
return width * height
get_rectangle_area(width=5, height=8) #OK
w = 4, h = 7
get_rectangle_area(height=h, width=w) #순서가 바뀌어도 가능
#default_argument.py
def get_rectangle_area(width, height=5):
return width * height
get_rectangle_area(8) #40
get_rectangle_area(4, 7) #28
def ..(width=5, height)
와 같이 default argument가 non-default argument보다 우선되어서는 안된다. 반드시 default argument는 맨 마지막에 나와야한다.*
)를 사용하여 나타낸다. #variable_length_argument.py
def asterisk_test(a, b, *args):
return a * b + sum(args)
asterisk_test(1, 2, 3, 4, 5) #14
#kwargs.py
def kwargs_test_1(**kwargs):
print(kwargs)
print(type(kwargs))
kwargs_test_1(first=3, second=4, third=5)
# {'first': 3, 'second': 4, 'third': 5}
# <class 'dict'>
def kwargs_test_3(one, two, *args, **kwargs):
print(one * two + sum(args))
print(kwargs)
kwargs_test_3(3,4,5,6,7,8,9, first = 3, second = 4, third = 5)
# 47
# {'first': 3, 'second': 4, 'third': 5}
*
)
#asterisk_for_unpacking.py
def asterisk_test(a, *args):
print(a, *args)
print(a, args)
print(type(args))
test = (2,3,4,5,6)
asterisk_test(1, *test) #Unpacking 이후(총 5개) 들어감
asterisk_test(1, test) #그냥 tuple 자체(총 1개)가 들어감
# 1 2 3 4 5 6
# 1 (2, 3, 4, 5, 6)
# <class 'tuple'>
# 1 (2, 3, 4, 5, 6)
# 1 ((2, 3, 4, 5, 6),)
# <class 'tuple'>