[Python] List, Dictionary
Updated:
1. List
List는 가장 기본적인 자료형이다.
- ordered: indexing이 가능하다
- mutable: 데이터 추가/삭제/삽입 등이 가능하다.
기본적인 사용방법은 건너뛰고 이번에는 Comprehension, filter함수, map함수 같이 사용하는 방법을 알아볼 것이다.
1.1 Comprehension
Comprehesion이란 sequence 자료형을 만드는 방법 중 하나로 간결하다는 것과 빠르다는 장점이 있다. Python에서는 크게 4가지 Comprehension이 있다.
- List Comprehension
- Set Comprehension
- Dictionary Comprehension
- Generator Expression
Generator의 경우 Comprehension과 동일하지만 특별히 expression이라고 부른다. 여기서는 List Comprehension과 Generator Expression에 대해 알아볼 것이다.
List Comprehesion의 기본적인 사용방법이다.
l1 = []
for i in range(10):
l1.append(i)
print(l1)
l2 = [i for i in range(10)]
print(l2)
결과
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
for-loop가 []
안으로 들어왔다. 따라서 코드의 길이가 어느정도 줄었다는 것을 알 수 있다.
다음은 2로 나누었을 때 나머지가 0인 수로 이루어진 list를 만드는 방법이다.
l2 = [i for i in range(10) if i % 2 == 0]
print(l2)
결과
[0, 2, 4, 6, 8]
if-condition도 list안에 넣어서 코드가 줄어든 것을 알 수 있다. 이 외에도 double for-loop문을 사용한다던지 등의 List Comprehension을 적용할 수 있다.
Generator Expression에 대해 알아보기 전에 Generator에 대해 알아보자.
Generator는 iterator를 생성해주는 함수이다. iterator는 next()
메소드를 이용하여 순차적으로 접근이 가능한 object이다.
list와 가장 큰 차이점은 list는 한 번에 여러가지 항목에 접근할 수 있지만 generator는 iterator를 반환하기 때문에 next()
를 이용해 한 번에 한가지 항목만 접근할 수 있다.
generator는 함수 안에서 yield를 사용하여 구현하며 yield에는 값(변수)를 지정한다.
def generator(n):
for i in range(n):
yield i
g = generator(5)
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
결과
0
1
2
3
4
기본적으로 함수를 이용해서 구현하지만 Generator Expression을 이용하면 더 간단하게 구현할 수 있다.
g = (i for i in range(5))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
g = (i for i in range(5))
for x in g:
print(x)
결과
0
1
2
3
4
0
1
2
3
4
generator는 for-loop로 접근할 수 있다. generator는 나중에 조금 더 자세히 다룰 것이다.
1.2. filter, map 함수와의 사용
filter 함수는 특정 조건으로 걸러진 요소들로 iterator객체를 만들어 반환하는 함수이다. map 함수는 반복가능한 객체를 받아서 각 요소에 함수를 적용해준다.
filter(function,object)
- function: 조건으로 쓰일 함수
- object: filter함수가 쓰일 대상
map(function,object)
- funtion: 적용할 함수
- object: function을 적용할 대상
# filter 사용 예시
a = [1,2,3,4,5]
for i in filter(lambda x : x % 2 == 0, a):
print(i,end=' ')
print()
# map 사용 예시
a = ['1','2','3','4','5']
for i in map(int,a):
print(type(i),end=' ')
print()
# filter와 map을 같이 사용한 예시
a = ['1','2','3','4','5']
b = list(filter(lambda x : x % 2 == 0,map(int,a)))
print(b)
결과
2 4
<class 'int'> <class 'int'> <class 'int'> <class 'int'> <class 'int'>
[2, 4]
1.3. 깊은 복사와 얕은 복사
List를 사용할 때 깊은 복사와 얕은 복사의 차이점을 알아두는 것이 좋다. 종종 복사한 객체의 값을 수정하려고 할 때 깊은 복사와 얕은 복사의 차이를 잘 몰라서 의도치 않은 상황이 발생할 수 있다.
얕은 복사(Shallow Copy): 객체를 복사할 때 해당 객체의 주소값만을 복사 깊은 복사(Deep Copy): 객체를 복사할 때 객체가 지닌 인스 턴스 변수 등 값 자체를 복사
둘의 차이점은 얕은 복사는 복사된 객체가 원본 객체와 같다는 것이고 깊은 복사는 복사된 객체가 원본 객체와 전혀 다르다는 차이점이 있다.
다음 예시를 보자.
l1 = [[10] * 3 for _ in range(3)] # 깊은 복사
l2 = [[10] * 3] * 3 # 얕은 복사
l1[0][1] = 3
l2[0][1] = 3
print(l1)
print(l2)
for i in l1:
print(id(i),end = ' ')
print()
for i in l2:
print(id(i),end = ' ')
print()
결과
[[10, 3, 10], [10, 10, 10], [10, 10, 10]]
[[10, 3, 10], [10, 3, 10], [10, 3, 10]]
140728217315712 140728217304320 140728197417280
140728217313664 140728217313664 140728217313664
l2는 얕은 복사를 적용했기 때문에 l2[0][1] = 3
이 의도치 않게 l2의 모든 원소 리스트의 1번째 원소를 3으로 변경하였다.
각 원소의 id값을 출력해보면 알 수 있듯이 깊은 복사는 모든 원소의 id값이 다르지만 얕은 복사는 모든 원소의 id값이 동일하여 l2의 모든 원소는 동일한 list임을 알 수 있다.
2. Dictionary
Python에서 Dictionary는 Hashtable을 구현한 자료구조이다. Hashtable은 key에 value를 저장하는 구조로 key를 이용하여 value에 빠르게 접근할 수 있다는 장점이 있다. key는 유일한 값이어야하며 중복을 허용하지 않는다. 그러면 어떤 자료형이 key가 될 수 있을까?
2.1. key가 될 수 있는 자료형
key가 될 수 있는 자료형은 기본적으로 str, int, float등 우리가 알고있는 기본적인 자료형이다. 여기까지만 된다면 Dictionary가 특별한 것처럼 보이지 않는데 추가로 immutable type 즉, 수정이 불가능한 자료형도 될 수 있다. 우리가 보통 알고 있는 자료형을 수정 가능한지 아닌지 나누어보았다
- 가변형 : list, bytearray, array.array, meomoryview, collections.deque
- 불변형 : tuple, str, bytes
더 구체적으로 확인하는 방법은 hash()
를 활용해서 알아보는 방법이 있다.
t1 = (10,20,(30,40,50))
t2 = [10,20,[30,40,50]]
print(hash(t1))
print(hash(t2))
결과
465510690262297113
TypeError: unhashable type: 'list'
hash()
는 hashable이 가능한 객체만을 받는다. unhashable type은 key가 될 수 없다.
2.2 setdefault와 Dictionary Comprehension
Dictionary를 만드는 기본적인 방법은 건너뛰고 setdefault()
와 Dictionary Comprehension를 활용하여 dictionary를 만드는 방법에 대해 알아보자.
Dictionary는 key의 중복을 허락하지 않는다고 하였다. 그렇다면 중복된 key가 들어올다면 dictionary는 과연 어떻게 동작할까?
source = [('k1','val1'),('k1','val2'),('k2','val3'),('k2','val4'),('k2','val5')]
d1 = {}
for k,v in source:
d1[k] = v
# Dictionary Comprehension
d2 = {k:v for k, v in source}
print(d1)
print(d2)
결과
{'k1': 'val2', 'k2': 'val5'}
{'k1': 'val2', 'k2': 'val5'}
먼저 Dictionary Comprehension을 이용해 구성한 Dictionary를 보자. 코드의 길이가 훨씬 줄어든 것을 볼 수 있다.
다시 본론으로 돌아가서 key가 중복된다면 나중에 들어온다는 값으로 변경됨을 알 수 있다. 그러면 중복된 key가 들어오는 모든 value를 하나의 list로 만드는 dictionary를 만들려면 어떻게 해야할까?
다음과 같이 구성할 수 있을 것이다.
source = (('k1','val1'),('k1','val2'),('k2','val3'),('k2','val4'),('k2','val5'))
newDict1 = {}
for k,v in source:
if k in newDict1.keys():
newDict1[k].append(v)
else:
newDict1[k] = [v]
print(newDict1)
결과
{'k1': ['val1', 'val2'], 'k2': ['val3', 'val4', 'val5']}
그런데 setdefault 메소드를 이용하면 더 간단하게 구현할 수 있다.
setdefault(key,default) : key가 dictionary에 있다면 해당 value를 반환, key가 없으면 default를 반환하는 함수
- key: key로 쓰이는 변수
- default: key가 dictionary에 없을 때 쓰이는 초기값
source = (('k1','val1'),('k1','val2'),('k2','val3'),('k2','val4'),('k2','val5'))
newDict2 = {}
for k,v in source:
newDict2.setdefault(k,[]).append(v)
print(newDict2)
결과
{'k1': ['val1', 'val2'], 'k2': ['val3', 'val4', 'val5']}
Leave a comment