10. 리스트의 활용




1 리스트 정렬하기


1-1 리스트의 sort 메소드

  • L.sort() 함수는 리스트 L 자체를 변경하며 리턴값을 반환하지 않는다.
In [1]:
L = [1, 5, 3, 9, 8, 4, 2]
print(L.sort())
print(L)
None
[1, 2, 3, 4, 5, 8, 9]
  • 파이썬2에서는 cmp(a,b) 내장 함수가 지원되었으나,
  • 파이썬3부터는 cmp(a,b) 내장 함수가 지원되지 않는다.
In [1]:
print(cmp(1,2))

print(cmp(5,2))

print(cmp('abc', 'abc'))


# -1
# 1
# 0
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-1-5ef27dd82ac6> in <module>()
----> 1 print(cmp(1,2))
      2 
      3 print(cmp(5,2))
      4 
      5 print(cmp('abc', 'abc'))

NameError: name 'cmp' is not defined
In [3]:
# cmp(a, b)
#   if a < b: return -1
#   if a > b: return 1
#   if a == b: return 0
def cmp(a, b):
    return (a > b) - (a < b) 

print(cmp(1, 2))

print(cmp(5, 2))

print(cmp('abc', 'abc'))
-1
1
0
  • Python2: 기본 정렬 방식을 변경하려면 cmp(a, b)와 같은 비교 함수를 직접 만들어서 sort() 함수의 인자로 넣는다.
  • Python3: 기본 정렬 방식을 변경할 수 없다.
In [4]:
def mycmp(a, b): # 대소관계에 따른 순서를 반대로 바꾸었음
    return cmp(b, a)

L = [1, 5, 3, 2, 4, 6]
print(L)
L.sort(cmp=mycmp) # 역순으로 정렬
[1, 5, 3, 2, 4, 6]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-5f12d79ed6fb> in <module>()
      4 L = [1, 5, 3, 2, 4, 6]
      5 print(L)
----> 6 L.sort(cmp=mycmp) # 역순으로 정렬

TypeError: 'cmp' is an invalid keyword argument for this function
  • 여러 튜플을 요소로 지닌 리스트인 경우, 튜플의 첫번째 값이 아닌 다른 위치에 있는 값을 기준으로 정렬
In [5]:
#Python2 기준 예제
def cmp_1(a1, a2):
    return cmp(a1[1], a2[1])

def cmp_2(a1, a2):
    return cmp(a1[2], a2[2])

L = [('lee', 5, 38), ('kim', 3, 28), ('jung', 10, 36)]
L.sort()
print('sorted by name:', L)

L.sort(cmp=cmp_1)
print('sorted by experience:', L)

L.sort(cmp=cmp_2)
print('sorted by age:', L)
sorted by name: [('jung', 10, 36), ('kim', 3, 28), ('lee', 5, 38)]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-5-1c604fbb67b8> in <module>()
     10 print('sorted by name:', L)
     11 
---> 12 L.sort(cmp=cmp_1)
     13 print('sorted by experience:', L)
     14 

TypeError: 'cmp' is an invalid keyword argument for this function
  • [중요] Python3: sort의 인자인 key에 객체가 지닌 원소들 내에 키값을 지정하는 함수를 지정가능
    • key 인자에 함수가 할당되어 있으면 각 리스트 원소에 대해 기본 비교함수 호출 직전에 key 함수를 먼저 호출한다.
In [9]:
#Python3 기준 예제
L = [('lee', 5, 38), ('kim', 3, 28), ('jung', 10, 36)]

def cmp_by_each_element(a):
    return a[1]

L.sort()
print('sorted by name:', L)

L.sort(key=cmp_by_each_element)
print('sorted by experience:', L)
sorted by name: [('jung', 10, 36), ('kim', 3, 28), ('lee', 5, 38)]
sorted by experience: [('kim', 3, 28), ('lee', 5, 38), ('jung', 10, 36)]
In [10]:
#Python3 기준 예제
S = ['loh', 'kim', 'jung']

def cmp_by_each_element(a):
    return a[1]

S.sort()
print('sorted by the first character:', S)

S.sort(key=cmp_by_each_element)
print('sorted by the second character:', S)
sorted by the first character: ['jung', 'kim', 'loh']
sorted by the second character: ['kim', 'loh', 'jung']
In [11]:
L = ['123', '34', '56', '2345']
L.sort()
print(L)

L.sort(key=int)
print(L)
['123', '2345', '34', '56']
['34', '56', '123', '2345']
  • sort() 함수 인자로 reverse 값을 받을 수 있다.
    • 디폴트 reverse 인자값은 False
    • reverse 인자값을 True로 주면 역순으로 정렬됨
In [12]:
L = [1, 6, 3, 8, 6, 2, 9]
L.sort(reverse=True) # L.sort(reverse = 1)
print(L)
L.sort(reverse=False) # L.sort(reverse = 1)
print(L)
[9, 8, 6, 6, 3, 2, 1]
[1, 2, 3, 6, 6, 8, 9]

1-2 sorted 내장 함수

  • sorted() 내장함수는 L 자체에는 내용 변경 없이 정렬이 되어진 새로운 리스트를 반환한다.
In [13]:
L = [1, 6, 3, 8, 6, 2, 9]
newList = sorted(L)
print(newList)
print(L)
[1, 2, 3, 6, 6, 8, 9]
[1, 6, 3, 8, 6, 2, 9]
In [14]:
for ele in sorted(L):
    print(ele, end=" ")
1 2 3 6 6 8 9 
  • Python2: sorted() 함수의 두번째 인자로 cmp를 지정하는 함수 할당 가능

    • 지정된 cmp 함수는 1, 0, -1만 Return
  • Python3: sorted() 함수의 두번째 인자로 key를 지정하는 함수 할당 가능

    • 지정된 key 함수가 할당되어 있으면 각 리스트 원소에 대해 기본 비교함수 호출 직전에 key 함수를 먼저 호출한다.
In [15]:
def mycmp(a1, a2):      # 대소관계에 따른 순서를 반대로 바꾸었음
    return cmp(a2, a1)

L = [1, 5, 3, 2, 4, 6]
print(sorted(L, key=mycmp))  # 역순으로 정렬
print(L)
# ['123', '2345', '34', '56']
# ['34', '56', '123', '2345']
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-15-e6a9c6532b35> in <module>()
      3 
      4 L = [1, 5, 3, 2, 4, 6]
----> 5 print(sorted(L, key=mycmp))  # 역순으로 정렬
      6 print(L)
      7 # ['123', '2345', '34', '56']

TypeError: mycmp() missing 1 required positional argument: 'a2'
In [13]:
#Python3 기준 예제
L = [ ('lee', 5, 38), ('kim', 3, 28), ('jung', 10, 36)]

def cmp_by_each_element(a):
    return a[1]

print(sorted(L))
print(L)

print()

print(sorted(L, key=cmp_by_each_element))
print(L)
[('jung', 10, 36), ('kim', 3, 28), ('lee', 5, 38)]
[('lee', 5, 38), ('kim', 3, 28), ('jung', 10, 36)]

[('kim', 3, 28), ('lee', 5, 38), ('jung', 10, 36)]
[('lee', 5, 38), ('kim', 3, 28), ('jung', 10, 36)]
In [14]:
#Python3 기준 예제
S = ['loh', 'kim', 'jung']

def cmp_by_each_element(a):
    return a[1]

print(sorted(S))
print(S)

print()

print(sorted(S, key=cmp_by_each_element))
print(S)
['jung', 'kim', 'loh']
['loh', 'kim', 'jung']

['kim', 'loh', 'jung']
['loh', 'kim', 'jung']
  • 인자로 reverse와 key 지정 가능
In [15]:
L = [1, 6, 3, 8, 6, 2, 9]
print(sorted(L, reverse=True))
[9, 8, 6, 6, 3, 2, 1]
In [16]:
L = ['123', '34', '56', '2345']
print(sorted(L))

print(sorted(L, key=int))

print(sorted(L, key=int, reverse=True))
['123', '2345', '34', '56']
['34', '56', '123', '2345']
['2345', '123', '56', '34']

1-3 L.reverse() 와 reversed() 내장 함수

  • L.reverse()도 반환값이 없다.
    • 즉, L 자체를 역순으로 뒤집는다.
    • [주의] 역순 정렬이 아니다.
In [17]:
L = [1, 6, 3, 8, 6, 2, 9]
print(L.reverse())
print(L)
None
[9, 2, 6, 8, 3, 6, 1]
In [18]:
L = [1, 6, 3, 8, 6, 2, 9]
L.reverse()       # 역순으로 뒤집는다.
for ele in L:
    print(ele + 2, end=" ")

print()
L.reverse()       # 다시 원상태로 복귀시킨다.
print(L)
11 4 8 10 5 8 3 
[1, 6, 3, 8, 6, 2, 9]
  • reversed() 내장함수는 sorted() 처럼 내용이 뒤집힌 리스트를 반환한다.
    • sorted() 처럼 원래 리스트 내용에는 변함없다.
In [19]:
L = [1, 6, 3, 8, 6, 2, 9]
print(L)
for ele in reversed(L):
    print(ele + 2, end=" ")
print()
print(L)
[1, 6, 3, 8, 6, 2, 9]
11 4 8 10 5 8 3 
[1, 6, 3, 8, 6, 2, 9]

2 리스트 내포(List Comprehension)


  • 다음은 고등학교 수학에서 배운 집합의 표기 방법이다.

    • A = {x^2 | x in {0, ..., 9}}
    • B = {1, 2, 4, 8, 16, ..., 2^16}
    • C = {x | x in S and x is odd}
  • 파이썬의 리스트 내포(list comprehension)는 바로 위 집합 표기법과 유사한 방식의 리터럴이다.

  • 위 집합들을 리스트 내포 방식으로 표현하면 다음과 같다.
    • A = [x**2 for x in range(10)]
    • B = [2**i for i in range(17)]
    • C = [x for x in S if x % 2 == 1]
  • 일반적인 리스트 생성법
In [20]:
L = []
for k in range(10):
    L.append(k*k)
    
print(L)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
  • 위 코딩은 리스트 내포 리터럴 방식을 활용해서 아래와 같이 변경할 수 있다.
In [21]:
L = [k * k for k in range(10)]
print(L)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
  • 리스트 내포 리터럴

    [expression for expr1 in sequence1
                for expr2 in sequence2 
                ...
                for exprN in sequenceN
                if condition]
    
  • 위 리터럴은 다음의 일반적인 for 문의 리털러과 동일

    l = []
    for expr1 in sequence1:
      for expr2 in sequence2:
        ...
           for exprtN in sequenceN:
              if condition:
                 l.append(expression)
    
  • expression의 평가 결과 반드시 한 개의 원소가 나와야 한다.

    • 틀린 예: [ x, y for x in seq1 for y in seq2 ]
  • 만약 두 개의 이상의 평가 결과가 나오면 튜플 등으로 감싸 주어야 한다.

    • 올바른 예: [ (x, y) for x in seq1 for y in seq2 ]
In [22]:
L = [k * k for k in range(10) if k % 2]   # 홀수의 제곱만 리스트로 형성
print(L)
[1, 9, 25, 49, 81]
  • 위의 리스트 내포 코드는 아래와 동일
In [23]:
L = []
for k in range(10):
    if k % 2:
        L.append(k*k)
print(L)
[1, 9, 25, 49, 81]
  • 두 개의 시퀀스 자료형에 대해 각각의 원소에 대한 쌍을 튜플 형태로 만들면서 리스트에 저장하는 코드
In [24]:
seq1 = 'abc'
seq2 = (1, 2, 3)
print([(x, y) for x in seq1 for y in seq2])
[('a', 1), ('a', 2), ('a', 3), ('b', 1), ('b', 2), ('b', 3), ('c', 1), ('c', 2), ('c', 3)]
In [25]:
seq1 = 'abc'
seq2 = (1, 2, 3)
l = []
for x in seq1:
    for y in seq2:
        l.append((x, y))
print(l)        
[('a', 1), ('a', 2), ('a', 3), ('b', 1), ('b', 2), ('b', 3), ('c', 1), ('c', 2), ('c', 3)]
  • 20보다 작은 2의 배수와 3의 배수에 대해 그 두 수의 합이 7의 배수인 것들에 대해 그 두 수의 곱을 출력하는 코드
In [26]:
L = [(i, j, i*j) for i in range(2, 20, 2) for j in range(3, 20, 3) if (i + j) % 7 == 0]
print(L)
[(2, 12, 24), (4, 3, 12), (6, 15, 90), (8, 6, 48), (10, 18, 180), (12, 9, 108), (16, 12, 192), (18, 3, 54)]
  • 문장 내 단어의 길이 출력
In [27]:
words = 'The quick brown fox jumps over the lazy dog'.split()
stuff = [[w.upper(), w.lower(), len(w)] for w in words]
for i in stuff:
    print(i)
['THE', 'the', 3]
['QUICK', 'quick', 5]
['BROWN', 'brown', 5]
['FOX', 'fox', 3]
['JUMPS', 'jumps', 5]
['OVER', 'over', 4]
['THE', 'the', 3]
['LAZY', 'lazy', 4]
['DOG', 'dog', 3]
  • 피타고라스 삼각형의 각 3변의 길이 리스트 (조건: 각 변 x, y, z의 길이는 30 보다 작아야 하며, x <= y <= z 이어야 함)
In [17]:
k = [(x,y,z) for x in range(1,30) for y in range(x,30) for z in range(y,30) if x**2 + y**2 == z**2]
print(k)
[(3, 4, 5), (5, 12, 13), (6, 8, 10), (7, 24, 25), (8, 15, 17), (9, 12, 15), (10, 24, 26), (12, 16, 20), (15, 20, 25), (20, 21, 29)]

참고 문헌: 파이썬(열혈강의)(개정판 VER.2), 이강성, FreeLec, 2005년 8월 29일