# Comprehension

 


 

1. list comprehension

 

파이썬에서 for문은 사용할 수 있는 범위가 굉장히 넓습니다. 예를 들어 리스트를 하나 만들고 그 안에 값을 동적으로 추가해야 하는 상황이라면 아래와 같은 방법을 이용할 수 있겠죠.

myList = []

for i in range(10):
	myList.append(i) # [0,1,2,3,4,5,6,7,8,9]

 

만약에 리스트에 값을 삽입하는 규칙이 있다면, 리스트를 선언하면서 값을 삽입 할 수 있으면 편하지 않을까요? 파이썬에서는 이를 Comprehension이라고 하고 리스트나 딕셔너리에 적용하는 것이 가능합니다.

 

myList = [i for i in range(10)] # [0,1,2,3,4,5,6,7,8,9]

 

위의 코드가 바로 list comprehension 입니다. 리스트를 선언함과 동시에 리스트 안에 for 문을 넣어 요소를 저장하는 방법인데요 for 문의 i가 늘어남과 동시에 i의 값이 저장되는 모습입니다. 즉 가장 앞에 명시한 변수에 저장된 값이 반복문이 실행되면서 리스트의 요소로써 저장되는 것입니다.

 

만약 list comprehension에 for문만 사용이 가능하다고 한다면 의미가 없는 구문일 수 있습니다. 역시 파이썬 제작자는 이를 간파하고 if else등의 제어문도 사용할 수 있게 만들었습니다.

 

#if
myList = [i for i in range(10) if i%2==0] # [0,2,4,6,8]

#if else
myList = [i if i%2==0 else i-1 for i in range(10)] # [0,0,2,2,4,4,6,6,8,]

#nested loop
myList = [j for i in range(3) for j in range(3)] # [0,1,2,0,1,2,0,1,2]

위 의 코드를 보면 if 와 if - else를 쓸 때의 문법의 차이가 있다는 것을 느끼셨을 텐데요, if 는 for문 뒤에오는 반면 if-else는 for 문 전에 옵니다. 그리고 if문은 조건문에 부합하는 요소만 저장하는 반면에 if-else는 조건문에 부합하지 않는 요소도 값을 변형시켜서 저장할 수 있습니다.(i-1)

 

list comprehension은 편리한 기능이지만, 이를 무분별하게 사용하는 것은 코드의 가독성을 해칩니다. 따라서 조건문이 다중이거나 nested된 반복문이라면 list comprehension을 지양하는 것이 좋습니다.

 


 

2. dictionary comprehension

 

사실 dictionary는 list 만큼 comprehension이 유용하지 않습니다. 하지만 길이가 같은 리스트 2개를 각각 Key Value로 삼아 딕셔너리로 형변환 하고자 한다면 유용하게 사용할 수 있습니다.

 

아래의 코드를 보시면 for문을 이용하여 길이가 같은 리스트 2개를 딕셔너리로 만드는 방법이 기재 되어있습니다. 기존의 코드는 리스트의 크기만큼 for문을 실행시켜서 딕셔너리에 각각의 Key, Value로 저장하는 방법인데요, 저는 개인적으로 Dictionary comprehension이 코드의 길이도 짧고 특정상황에서는 훨씬 가독성이 좋다고 생각합니다.

myList_1 = [1,2,3]
myList_2 = ['a','b','c']

# original code
newDict = dict()
for i in range(len(myList_1)):
    newdict[myList_1[i]] = myList_2[i]

# dictionary comprehension
newDict = {myList_1[i]: myList_2[i] for i in range(len(myList_1))}

 

Dictionary comprehension은 dictionary의 Key-Value를 반전시키는데도 사용할 수 있습니다. dictionary class의 메소드로 제공되고 있는 items()는 dictionary의 모든 key,value쌍을 tuple로 상속된 객체를 리턴합니다. 해당 객체는 iteration 클래스이기 때문에 for문의 iterable 객체로 사용할 수 있습니다. 그렇기 때문에 다음과 같은 방식을 이용하면 dictionary  Key-Value를 반전 시킬 수 있습니다.

 

# original code
myDict = {1: 'a', 2: 'b', 3: 'c'}
newDict = dict()
for key,value in myDict.items():
    newDict[value] = key

# dictionary comprehension
myDict = {1: 'a', 2: 'b', 3: 'c'}
newDict = {value : key for key,value in myDict.items()}

 

 


 

3. zip함수를 이용한 자료형 변환

 

파이썬에서 기본으로 제공하고 있는 zip함수는 리스트를 딕셔너리로 변경해주거나 딕셔너리의 Key-Value를 반전시키는데 가장 효율적인 함수입니다. 

# List to dictionary
myList_1 = [1,2,3]
myList_2 = ['a','b','c']
newDict = dict(zip(myList_1,myList_2))

# Key-Value reverse
myDict = {1: 'a', 2: 'b', 3: 'c'}
reverse_dictionary = dict(zip(myDict.values(),myDict.keys()))

위 코드는 zip함수를 이용하여 list 2개를 dictionary로 변환시키는 코드입니다. Dictionary comprehension이나 일반적인 for문으로 변환 시키는것보다 코드의 길이도 짧고 가독성도 훌륭합니다. 

또한 마지막 라인을 보시면 zip함수를 이용하여 dictionary를 반전시켰습니다. dictionary class의 메소드인 values()와 keys()를 이용하여 모든 value와 key를 추출하고 zip함수의 첫 번째, 두 번째 인자로 넣습니다.

여기까지만 하면 zip 클래스의 인스턴스가 되게 됩니다. 이 객체는 Iterable하지만 dictionary라고 할 수 없죠, 그래서 전체 구문을 dict로 감싸 형변환을 마무리합니다.

 

 

'Dev > python' 카테고리의 다른 글

[python] 클래스(class)와 인스턴스  (1) 2019.04.07
[python] 함수  (0) 2019.04.04
[python] 제어문(if, for, while) - 3  (0) 2019.03.23
[python] 시퀀스 자료형과 이터러블  (1) 2019.03.23
[python] 제어문(if, for, while) - 2  (0) 2019.03.23

# 파이썬 리스트



리스트란 여러가지 데이터를 저장하는 자료구조입니다. 리스트를 이용하면 숫자나 문자열 등의 데이터를 다량의 변수를 선언하지 않고도 간단하게 표현할 수 있습니다.

무엇보다 Python에서의 리스트는 자료형에 구애받지 않고 자료형에 상관없이 한개의 리스트에 혼합하여 저장할 수 있는 장점이 있습니다.


1. 리스트의 생성 및 데이터 저장


1
2
3
4
5
6
arr1 = [1,2,3,4,5# ok
arr2 = ["a","b","c","d"# ok
arr3 = ["1",2,"3",4,"babo"# ok
arr4 = [1,2,[3,4,5,6]] # ok
arr5 = [] # 빈 리스트 생성
arr6 = list() # 빈 리스트 생성
cs



2. 인덱싱


리스트는 문자열과 같이 인덱싱을 적용하는것이 가능합니다. 리스트의 첫번째 요소를 0번 인덱스로 지정하고 마지막요소에 리스트의 크기-1 인덱스를 지정합니다.

- 참고 : 마지막 인덱스는 -1로 표현하고 1씩 감하여 이전 인덱스를 표현합니다.


1
2
3
4
5
arr = [1,2,3,4,5]
# a[0] == 1
# a[4] == 5
# a[-1] == 5
# a[-2] == 4
cs


만약, 리스트안에 다중으로 리스트가 들어있는 경우에는 인덱싱을 두번하여 중첩 리스트안에 있는 데이터에 접근할 수 있습니다.


1
2
3
arr = [1,2,[1,2,3]]
# arr[-1] == [1,2,3]
# arr[-1][-1] == [3]
cs



3. 슬라이싱


리스트는 문자열과 마찬가지로 인덱싱 뿐만아니라 슬라이싱 또한 가능합니다.  범위를 지정하여 시작 인덱스의 요소부터 마지막 인덱스의 요소로 구성된 리스트로 분할합니다.


1
2
3
arr=[1,2,3,4,5]
# arr[0:2] = [1,2]
# arr[2:] = [3,4,5]
cs


4. 리스트 요소 수정


리스트의 데이터를 변경하고 싶을 경우에는 해당 인덱스에 대입연산자를 사용하여 데이터를 삽입하면 됩니다.


1
2
3
arr = [1,2,3,4,5]
arr[0= 5
# arr == [5,4,3,2,5]
cs




# 객체의 복사


파이썬에서는 리스트도 객체에 해당하기 때문에 복사를 하게되면 바로보는 객체가 동일하기 때문에 두개의 리스트 중 하나만 변경해도 나머지 하나가 동일하게 수정되는 현상이 발생합니다.


1. shallow copy(얕은 복사)


1
2
3
4
5
6
7
8
9
10
11
= [1234]
= a
print(b)
b[2= 5
print(b)
print(a)
 
#결과값
[1234]
[1254]
[1254]
cs


a라는 변수를 선언하고 리스트를 만들어 a에 할당하면 a는 리스트객체의 주소가 저장된 변수가 됩니다.

그럼 a라는 변수의 값을 b라는 인자에 할당하였을 경우, b는 a와 같은 객체의 주소가 저장된 변수가 되는걸까요? 정답은 예,그렇습니다. b를 불러와 인덱스 2번의 값을 5로 변경하면, a에도 똑같이 적용되어 a의 값이 1,2,5,4가 되는 것을 위의 코드를 통하여 알 수 있습니다.



리스트는 값을 대입하면 값에 대한 메모리가 새로이 할당되는 것이 아닌 기존 값의 메모리 주소를 공유하기 때문에 발생하게 됩니다. 리스트 같은 경우 리스트 자체뿐만 아니라 리스트 내 요소들도 같은 주소를 공유하고 있습니다. 이를 Shallow Copy(얕은 복사)라고 부릅니다.



2. Deep Copy(깊은 복사)


이러한 현상을 방지하기 위해서는 Deep copy를 사용하여 복사하면 됩니다. 방법은 간단합니다. 기본라이브러리인 copy를 사용하는 방법이 있지만 더욱 간단한 사용을 위해 다른방법을 설명드리겠습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#1번 방법
= [1234]
= a[:]#리스트 슬라이싱
print(b)
b[2= 5
print(b)
print(a)
 
#2번 방법
= [1234]
= list(a)#리스트 내장함수사용
print(b)
b[2= 5
print(b)
print(a)
 
#결과값(1번과 2번 동일)
[1234]
[1254]
[1234]
cs


간단하게 리스트를 슬라이싱하여 대입하면 shallow copy가 일어나지 않습니다. 또 list 내장함수를 사용하면 Deep copy가 진행되어 리스트 복사문제를 해결 할 수 있습니다.

'Dev > python' 카테고리의 다른 글

[python] 딕셔너리(dictionary)  (0) 2019.02.13
[python] 리스트 함수  (0) 2019.02.11
[python] 문자열 함수  (0) 2019.02.09
[python] 문자열  (0) 2019.02.07
[python] 기본자료형 - 숫자  (0) 2019.02.07

+ Recent posts