자료구조
스택(Stack)
파이썬의 리스트를 사용해 스택구조를 구현 가능하다.
push : list.append()
pop : list.pop()
큐(Queue)
큐도 스택과 마찬가지로 리스트를 사용해 구현이 가능하고 pop의 동작만 인자를 주어 가장 앞의 값을 pop하도록 하면된다.
push : list.append()
pop : list.pop(0)
튜플(Tuple)
값의 변경이 불가능한 리스트, t = (1, 2, 3)
과 같이 소괄호를 사용해 튜플을 표현할 수 있으며 값이 하나인 튜플은 t = (1, )
와 같이 ,(콤마)를 붙혀줘야한다.
셋(Set)
값을 순서없이 저장하며, 중복이없는 자료형이다.(Set 함수)
사전(dictionary)
Key, Value의 쌍을 저장하는 자료형
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import csv
def getKey(item):
return item[1]
command_data = []
with open("command_data.csv", "r", encoding="utf-8") as csvfile:
spamreader = csv.reader(csvfile, delimiter=",", quotechar='"')
for row in spamreader:
command_data.append(row)
command_counter = dict()
for data in command_data:
if data[1] in command_counter.keys():
command_counter[data[1]] += 1
else:
command_counter[data[1]] = 1
dictlist = list()
for key, value in command_counter.items():
temp = [key, value]
dictlist.append(temp)
sorted_dict = sorted(dictlist, key=getKey, reverse=True)
print(sorted_dict)
Collections
list, tuple, dict에 대한 python built-in 모듈
deque
list에 비해 효율적인 방식을 지원(리스트보다 처리 속도가 높음)
기존 list형태의 함수를 모두 지원하고 rotate, reverse등의 linked list의 특성을 지원한다.
1 2 3 4 5 6 7 8 9 10 11
from collections import deque dq = deque() dq.append(4) dq.appendleft(5) # 앞에 저장 가능 dq.rotate(1) # 1칸씩 회전 dq.extend([5,6]) dq.extendleft([5,6]) # 앞에 리스트 추가
OrderedDict
지금 dict는 이미 순서를 보장하면서 출력이 가능하다. (3.5버전 이후)
defaultdict
dict는 없는 key를 사용할 때 KeyError가 발생하는데 defaultdict 는 default값을 설정해 없는 키에 대해 default값을 줄 수 있다.
1 2 3 4 5
from collections import defaultdict d = defaultdict(object) d = defaultdict(lambda:0) # default 값을 0으로 설정, 함수 형태로 넣어주어야함 d["xcasda"] # 0
Counter
Sequence type의 원소들의 갯수를 dict형태로 반환해준다.
1 2 3 4 5 6 7 8 9 10 11 12 13
from collections import Counter s = "aaaabbb" c = Counter(s) # {'a':4,'b':3} list(c.elements()) # 각 원소를 개수만큼 리스트로 만들어줌 #Counter자료형끼리 연산도 가능하다 a = "aabbb" b = "abbb" a_c = Counter(a) b_c = Counter(b) a_c - b_c # Counter({'a': 1})
namedtuple
tuple형태로 구조체를 저장하는 방법이다.
1 2 3 4 5 6
from collections import namedtuple Point = namedtuple("Point", ['x', 'y']) p = Point(1,2) p.x # 1 p.y # 2
list comprehension
기존 list를 사용해 빠르게 다른 list를 만드는 방법이다. for + append보다 좀더 나은 속도를 보여준다.
일반적인 for loop 방법
1 2 3
result = [] for i in range(10): result.append(i)
list comprehension 방법
1
result = [i for i in range(10)]
가장 앞의 값(i)을 리스트에 넣는것이고 그 뒤에는 i값에 대한 코드가 작성되어있다.
result에 i를 넣을것인데 i는 0 ~ 9의 값 이라는것을 알 수 있다.
list comprehension Nested For loop
1 2 3 4 5
s1 = "ABCDEF" s2 = "abcdef" result = [i+j for i in s1 for j in s2] #for i in s1: # for j in s2: 로 이해하면된다.
Filtering
1 2 3 4 5 6 7 8
#1 result = [i for i in range(10) if i % 2 == 0] #2 s1 = "ABC" s2 = "BCD" result = [i+j for i in s1 for j in s2 if not(i==j)] # result = ['AB', 'AC', 'AD', 'BC', 'BD', 'CB', 'CD']
조건을 추가해 조건에 맞는 값만 추가할 수있다.
Filter에 걸린 조건에 대해 다른 작업을 추가하기 위해서는 조건문을 앞으로 가져와 삼항연산자 처럼 조건을 작성해주면된다.
1 2 3 4
s1 = "ABC" s2 = "BCD" result = [i+j if not(i==j) else "NOT" for i in s1 for j in s2] # result = ['AB', 'AC', 'AD', 'NOT', 'BC', 'BD', 'CB', 'NOT', 'CD']
Two dimensional list
1 2 3 4
s1 = "ABC" s2 = "BCD" result = [[i+j for i in s1] for j in s2] # result = [['AB', 'BB', 'CB'], ['AC', 'BC', 'CC'], ['AD', 'BD', 'CD']]
for j in s2
loop가 먼저 돌게된다
zip
두개의 list의 값을 병렬적으로 추출할 때 사용
1
2
3
4
5
6
7
a = ['a', 'b', 'c']
b = ['A', 'B', 'C']
[[a_val,b_val] for a_val,b_val in zip(a, b)]
# [['a', 'A'], ['b', 'B'], ['c', 'C']]
[c for c in zip(a,b)]
# [('a', 'A'), ('b', 'B'), ('c', 'C')] # 튜플 타입으로
lambda
함수 이름 없이 함수처럼 쓸 수 있는 익명함수
1
2
3
4
5
6
7
8
9
10
11
# 일반적인 함수
def f(x,y):
return x + y
f (1, 4)
# lambda
f = (lambda x, y : x + y)
f(1, 4)
# lambda
(lambda x, y : x + y)(1,4)
map, reduce
map
map함수를 사용하면 iterator로 결과를 받을 수 있다. map은 순회가능한 자료형의 자료를 하나씩 꺼내서 함수를 적용시킨 뒤 새로운 리스트를 만들어준다.
map(함수, elements)
형식으로 사용한다. map은 lazy evaluation을 진행해서 메모리를 절약할 수 있다.1 2 3
a = [3.4, 5.2, 2.1] a = list(map(int, a)) # a = [3, 5, 2]
reduce
여러개의 데이터를 누적해 작업할때 사용한다.
reduce(함수, elements, init)
형식으로 사용한다.1 2 3 4 5 6 7
from functools import reduce def get_sum(x, y): return x+y s = [1,2,3,4,5,6,7] reduce(get_sum,s) # 28 init은 default가 0이다.
처음 x y로 1 2가 들어가고 다음 x y로는 3(1 + 2), 3이 들어간다.
filter
map과 마찬가지로 iterator로 결과를 반환해주는 함수이다.
filter(함수, elements)
형식으로 사용한다. 함수 결과가 True가 되는 element만 넣어준다.
1
2
3
4
5
s = [1,2,3,4,5]
def is_even(n):
return True if n % 2 == 0 else False
test = filter(is_even, s)
iterator
iter함수를 사용해 iterable한 객체(sequence객체)를 iterator객체로 만들 수 있고, iterator 객체는 next함수를 가지고있다. next를 사용할때마다 다음 요소를 가져올 수있다. (더이상 없는경우 Stopiteration 예외)
iterator는 상태를 가지고있다. next함수를 호출했을 때 어디로 가야하는지에 대한정보와 현재 가리키고 있는 값의 정보를 가지고있다. 사용되는 순간 자신이 가진 값을 반환할 수 있다.
Geneartor
iterable object를 특수한 형태로 사용해주는 함수, element가 사용되는 시점에 값을 메모리에 반환
메모리를 절약할 수 있다. Generator는 데이터 반환을 위해 return이 아닌 yield를 사용한다.
일반함수는 return을 만나면 종료되고, 자신이 사용했던 메모리를 해제한다. 하지만 geneartor함수는 yield를 만나면 현재 상태를 유지하고 정지되며 함수를 호출한 곳에 yield로 명시한 값을 반환한다.
1
2
3
4
5
6
def generator_list(value):
for i in range(value):
yield i
for a in generator_list(50):
print(a) # 이 순간에 yield가 값을 단져줌
generator함수를 처음 호출하면 함수 내부의 i가 for loop를 돌기 시작한다.
함수 내부에서는 처음 i == 0일 때 yield를 만나서 i를 반환하게되고 이 값은 a에 저장되어 출력된다.
다시 한번 generator함수가 호출되고 이전에 정지 되었던 상태인 i == 0 인상태에서 다음 동작을 진행한다. i == 1이되고 다시 yield를 만나 이전의 절차가 반복된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def generator_list(value):
for i in range(value):
yield i
result = generator_list(10)
print(list(result))
print(list(result))
def test_list(value):
result = []
for i in range(value):
result.append(i)
return result
result1 = test_list(10)
print(result1)
print(result1)
# output
# [0,1,2,3,4,5,6,7,8,9]
# []
# [0,1,2,3,4,5,6,7,8,9]
# [0,1,2,3,4,5,6,7,8,9]
위의 코드를 보면 더 이해하기가 쉽다. result는 generator_list함수가 i == 0인 상태의 iterator를 가지고있게된다. 이후 print에서 list로 변환해서 쓸 때 generator_list함수가 끝까지 돌게되어 다음 print할때 result는 더이상 다음이 없는 iterator가 되어 출력이 아무것도 되지않는다. 처음 메모리를 절약할 수 있다는것이 명확하게 이해가 되지않았는데 위의 코드를 보니 어떻게 절약이 되는지 알것같다.
Generator comprehension
1
gen = (n*n for n in range(500))
list comprehension과 비슷하며 괄호만 신경써주면된다.
언제 사용할까
- list 타입의 데이터를 반환해주는 함수는 generator로 만드는것이 좋다.
- 큰 데이터를 처리할 때는 generator expression을 고려해야한다.
- 파일 데이터를 처리할 때 generator를 사용하는것이 좋다.
Arguments
keyword argument
1 2 3 4
def fun(a, b, c): print(f"{a}{b}{c}") fun(c = "asdasd", a = "axxx", b = "sss")
위의 코드처럼 인자를 순서대로 넣지 않고 keyword를 사용해 원하는 순서로 넣을 수 있다.
default argument
1 2
def fun(a, b, c = "xxx"): print(f"{a}{b}{c}")
default값이 지정된 parameter가 있는경우 해당 parameter에 인자가 들어오지 않으면 지정된 값으로 사용된다.
가변인자
Asterisk기호를 사용해 parameter를 표시한다. 가변인자 parameter는 인자를 tuple type으로 받아온다.
1 2 3 4
def fun(a, b, *args): ..... fun(1,2,3,4,5)
위의 코드에서는 3, 4, 5가 tuple type으로 args에 들어간다.
키워드 가변인자
Parameter이름을 따로 지정하지 않고 입력하는 방법, Asterisk두개를 사용해 함수의 parameter를 표시한다. 키워드 가변인자는 dict type으로 입력된다.
1 2 3 4 5
def fun(**kwargs): print(kwargs) fun(a=3, b=4, c=5) # {'a':3, 'b':4, 'c':5}
unpacking a container
tuple, dict등 자료형에 들어가 있는 값을 unpacking하여 함수의 입력 혹은 zip등에 유용하게 사용할 수 있다.
1
2
3
4
5
6
7
8
def fun(a, *args):
..
fun(1, *(2,3,4)) # args는 3개의 입력을 받는다.
fun(1, (2,3,4)) # args는 1개의 입력을 받는다.(tuple type한개)
print(*["1", "2", "3", "4"])
# output : 1 2 3 4
Asterisk를 붙혀주면 unpacking이 일어나 튜플로 함수를 호출하지 않고 2,3,4의 인자로 함수를 호출한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
ex = ([1,2], [3,4], [5,6], [5,6])
for value in zip(ex): # ex는 하나의 tuple이므로 zip이 되지 않는다.
print(value)
# output
# ([1, 2],)
# ([3, 4],)
# ([5, 6],)
# ([5, 6],)
for value in zip(*ex)
print(value)
# output
# (1, 3, 5, 5)
# (2, 4, 6, 6)
정규식
정규식을 사용하면 코드를 더 간결하고 직관적으로 작성할 수 있다.
정규식에서 사용하는 메타문자
. ^ $ * + ? { } \ | ( )
대괄호([])
[] 는 괄호 사이의 있는 한개의 문자와 일치하는 것이 있을 경우 매칭된다.
-(하이픈)을 사용해 범위를 지정할 수 있고, ^(대괄호 안에서)은 not의 의미로 사용된다.
- \d (digit) : 숫자를 나타내며 [0-9]와 같다.
- \s (space) : 공백을 나타내며 white space 문자와 매칭된다.
- \w (word) : undersocre를 포함한 문자 숫자와 매칭된다.
- \D : \d의 반대, 숫자가 아닌것
- \S : \s의 반대
- \W : \w의 반대
- 참고 : 백슬래시를 사용할 경우 escape처리해주거나 문자열의 앞에 r문자를 삽입해 Raw string으로 사용해야한다.
반복
반복에는 *(Asterisk)와 +(plus), {}(중괄호), ?(물음표)가 있으며 *은 0부터, +는 1부터 반복한다.
{}는 {m, n}형식으로 나타내며 반복횟수가 m번부터 n번까지인 것과 매치된다. ?는 {0, 1}과 똑같다.
반복의 경우 ab*c 와 같은 형식으로 사용할 수 있고 b가 반복을 사용해 매치하려는 문자가 된다.
점(.)
개행 문자인 \n을 제외한 모든 문자와 매치된다.
예시
1
2
3
ab+c : a + 1번 이상의b + c
a.b : a + 모든문자 + b 와 매치
a[.]b : a.b와 매치
^, $
^는 문자열의 처음, $는 문자열의 끝을 의미한다.
|
정규표현식에서 Or
re모듈
파이썬에서 정규 표현식을 사용하기 위해서는 re모듈을 사용해야한다. re모듈의 compile을 사용해 정규식을 컴파일해서 나온 객체를 이용해 문자열 매칭을 할 수 있다.
compile
정규식을 이용해 패턴을 만드는 과정
1 2 3 4 5 6 7
import re p = re.compile('a*z') # compile options p1 = re.compile('a.z', re.DOTALL) # .이 개행 문자를 포함한다. p2 = re.compile('[a-z]', re.I) #대소문자 구별없이 매치 p3 = re.compile('^abc\s\w+', re.MULTILINE) # ^, $메타 문자를 각 줄마다 적용가능하게 하는 옵션
match 함수
문자열의 처음부터 매치되는지 확인한다. 매치되는 경우 match객체를 반환하고 매치되지 않을 경우 바로 None을 반환한다.
1 2 3
result = p.match("aaaaz") # <re.Match obejct;..> result = p.match("axz") # None result = p.match("xaaaaz") # None
문자열의 처음부터 매치를 확인하기 때문에 xaaaaz의 경우 일치하는 패턴이 있지만 터음 x에서 패턴이 일치하지 않으므로 None이 나온것을 볼 수 있다.
search 함수
문자열 전체를 검색하여 정규식과 매치되는지 확인한다. 매치되는 곳이 있다면 match객체를 반환하고 없는 경우 None를 반환한다.
1
result = p.search("xaaaaz") # <re.Match object;..>
match와 search가 반환하는 match객체는 group, start, end, span함수를 가지고있다.
- group() : 매치된 문자열을 반환한다.
- start() : 매치된 시작 위치를 반환한다.
- end() : 매치된 끝 위치를 반환한다.
- span() : 매치된 문자열의 start,end 튜플을 반환한다.
findall 함수
정규식과 매치되는 모든 문자열을 리스트로 반환한다.
1 2 3 4 5
result = p.findall("az z zzzzzz az aaaazzzzz asdzazazaza asadz") # result = ['az', 'z', 'z', 'z', 'z', 'z', 'z', 'z', 'az', 'aaaaz', 'z', 'z', 'z', 'z', 'z', 'az', 'az', 'az', 'z'] result = p.findall("zzzzz") # result = ['z', 'z', 'z', 'z', 'z']
finditer 함수
정규식과 매치되는 모든 문자열을 iterator로 반환한다.
그룹 (Group)
소괄호를 사용해 그룹을 나눌 수있다.
1
2
3
4
5
p = re.compile(r"(\w+)\s(\d+)") # w와 d를 그루핑
m = p.search("kms 2012122026")
m.group(0) # kms 2012122026
m.group(1) # kms
m.group(2) # 2012122026
그룹의 경우 많이질 떄 내가 원하는것의 index가 무엇인지 알기 어렵다. 이럴경우 그룹에 이름을 지어줄 수 있다. (?P<name>\w)
Sub
sub(바꿀 문자열, 대상 문자열)
형식이며 대상 문자열에서 매치되는 문자열을 바꿀 문자열로 변경한다.
1
2
3
4
5
6
p = re.compile('apple|banana')
p.sub("fruit", "I like apple and banana") # I like fruit and fruit
#compile하지 않고 맨 앞에 패턴을 작성해주어도 된다.
result = re.sub(r"[*%$#@]", "", "asdjhaskj1@#*%@ASD") # asdjhaskj1ASD
참고
jupyter notebook 에서 함수 시간 측정
timeit function_name()
이렇게 작성해주면 여러번 함수를 실행해 시간을 측정해준다.