05wk-2: 파이썬의 자료형 (2)

Author

최규빈

Published

April 5, 2024

1. 강의영상

2. Imports

pass

3. tuple

A. 리스트 vs 튜플

- 공통점: (1) 컨테이너형타입이라는 점, 그리고 (2) 연산 및 인덱싱을 하는 방법은 리스트와 같음 - 차이점1: [] 대신에 ()를 사용한다. - 차이점2: 불변형이다. (원소의 값을 바꿀 수 없음) - 차이점3: 하나의 원소를 선언할 때는 (1,)와 같이 해야 한다. - 차이점4: 의미가 명확할때는 튜플의 ()를 생략가능하다.

- 컨테이너형이라는 것이 무슨의미?

a=(4,6,'pencil', 3.2+4.6j, [3,4])
type(a[2])
str
type(a[3])
complex

- 연산의 유사성

(1,2) + (3,4,5)
(1, 2, 3, 4, 5)
(1,2) * 2
(1, 2, 1, 2)

- 인덱싱의 유사성

tpl = (11,21,31)
tpl[1:]
(21, 31)

- 차이점2: 불변형이라는 것은 무슨의미?

a[2] = 'Pencil'
TypeError: 'tuple' object does not support item assignment

참고로 a를 튜플이 아니라 리스트로 선언하면 값이 잘 바뀐다.

a=[4,6,'pencil', 3.2+4.6j, [3,4]]
a[2]
'pencil'
a[2]='Pencil'
a
[4, 6, 'Pencil', (3.2+4.6j), [3, 4]]

- 차이점3: 하나의 원소로 이루어진 튜플을 만들때는 쉼표를 붙여야 함.

[1]+[2,3,4]
[1, 2, 3, 4]
(1,)+(2,3,4)
(1, 2, 3, 4)

- 차이점4: 의미가 명확할때 튜플의 괄호는 생략가능하다. (이게 중요합니다)

a=1,2
a
(1, 2)

의미가 명확할때 생략해야함

1,2 + 3,4,5 
(1, 5, 4, 5)
(1,2) + (3,4,5) 
(1, 2, 3, 4, 5)

B. 선언

- 소괄호를 이용

a=(1,2,3)
a
(1, 2, 3)
type(a)
tuple

- 생략가능하다는 점이 포인트

a=1,2,3
a
(1, 2, 3)
type(a)
tuple

- 원소가 하나인 튜플을 만들고 싶다면?

a=(1,)
a
(1,)

C. 연산

- 리스트와 동일

(1,2)+(3,4,5)
(1, 2, 3, 4, 5)
(1,2)*2
(1, 2, 1, 2)

D. 인덱싱

- 리스트와 동일

a=(1,2,3,-4,-5)
a
(1, 2, 3, -4, -5)
a[-1]
-5
a[-3:]
(3, -4, -5)

E. 슬기로운 튜플사용 (\(\star\))

# 예제 – 여러변수를 동시에 출력하고 싶을 경우 (다중출력?)

변수를 아래와 같이 선언하였다고 하자.

a=1
b=2
c=3

선언된 값을 확인하려면?

a
1
b
2
c
3

튜플을 이용하면?

a,b,c # 괄호하나 생략하는것이 이렇게 편하다..
(1, 2, 3)

#

# 예제2 – 다중할당1 (여러개의 변수를 동시에 선언하고 싶을 경우)

아래와 같이 =를 5번 쓰면 5개의 변수를 선언할 수 있다.

name = 'Tom'
age = 20 
sex = 'M'
height = 180
weight = 70

튜플을 이용하면 좀더 간단히 하나의 = 로도 아래와 같이 선언할 수 있다.

name, age, sex, height, weight = 'Tom', 20, 'M', 180, 70  # 다중할당

#

# 예제2 – 다중할당2, 위도와 경도

coor = (37,127) # 서울 
coor
(37, 127)
lat, long = coor # 다중할당
lat 
37
long 
127

#

# 잠깐만 – 다중할당은 꼭 튜플에서만 가능한가?

그건 아니다…

[x,y,z] = [1,2,3] 
x,y,z # 다중출력 
(1, 2, 3)
[x,y] = 'hi'
x,y 
('h', 'i')

튜플과 같이 사용하면 가독성이 극대화 (그래서 다중할당은 거의 튜플과 세트로 사용함)

x,y,z = 1,2,3
x,y,z # 다중출력 
(1, 2, 3)
x,y = 'hi'
x,y 
('h', 'i')

#

# 예제 – 임시변수 사용없이 두 변수의 값을 교환

a=10
b=20
a,b
(10, 20)
a,b = b,a 
a,b
(20, 10)

#

# 예제 – for문과 튜플

lst = [['guebin', 202112345, 'M'],
       ['iu',202254321, 'F'],
       ['hodong', 202011223, 'M']]
lst
[['guebin', 202112345, 'M'],
 ['iu', 202254321, 'F'],
 ['hodong', 202011223, 'M']]
lst[0]
['guebin', 202112345, 'M']
for name,studentid,sex in lst: 
    print(name,sex)
guebin M
iu F
hodong M

#

# 예제 – for문과 튜플, dummy variable _

for name,studentid,sex in lst: 
    print(studentid)
202112345
202254321
202011223
for _,studentid,_ in lst: 
    print(studentid)
202112345
202254321
202011223
for _,_,sex in lst: 
    print(sex)
M
F
M
for name,_,sex in lst: 
    print(name,sex)
guebin M
iu F
hodong M
for name,_,_  in lst: 
    print(name)
guebin
iu
hodong

#

# 예제 – 튜플과 언패킹연산자 *

아래와 같이 관심없는것 모두를 other라는 이름으로 받을 수 있음

for name,*other  in lst: 
    print(name,other)
guebin [202112345, 'M']
iu [202254321, 'F']
hodong [202011223, 'M']

*의 동작을 더 세심히 살펴보자.

head, body, *tail = range(1,11) 
head, body, tail
(1, 2, [3, 4, 5, 6, 7, 8, 9, 10])
head1,head2, *body, tail1,tail2,tail3 = range(1,11) 
head1,head2, body, tail1,tail2,tail3 
(1, 2, [3, 4, 5, 6, 7], 8, 9, 10)
*head, body, tail = range(1,11) 
head, body, tail
([1, 2, 3, 4, 5, 6, 7, 8], 9, 10)

(관찰)

그러고 보니까..

[*head, body, tail] = [1,2,3,4,5,6,7,8,9,10] 
[head, body, tail] = [[1,2,3,4,5,6,7,8],9,10] 

이렇다는 거잖아?

*를 붙이면 1차원 자료구조가 풀린다..?

[1,2,[1,2,3]]
[1, 2, [1, 2, 3]]
[1,2,*[1,2,3]]
[1, 2, 1, 2, 3]

- 단독사용은 불가능

*[1,2,3]
SyntaxError: can't use starred expression here (386627056.py, line 1)

F. 튜플을 왜 쓸까? – 제 생각..

- 질문: 리스트를 쓰지 않고 왜 튜플을 써야하나?

- 답변1: (책의 설명)

  • 초보적인 설명: 실수방지
  • 더 정확한 설명: 빠르다, 여러사람과 작업하기 유리하다, 깊은복사/얕은복사시 원하지 않는 오류 (side effect 이라고 함) 방지

- 답변2: 괄호를 생략할 수 있기 때문에, 리스트 말고 튜플을 쓰면 편함.

  • 소괄호의 생략 + 언패킹 \(\Rightarrow\) 엄청난 가독성.
  • 컴공과 사람들 의견: 튜플 + 언패킹 \(\Rightarrow\) 엄청난 가독성 \(\Rightarrow\) 충격 \(\Rightarrow\) “파이썬 편하더라고요..”

- 근본적인 의문: 파이썬을 처음 만들때 리스트에 괄호를 생략하는 기능을 추가했으면 편했잖아?

- 저의 설명: 리스트=신라면, 튜플=라면사리 같은 느낌으로 이해하면 된다. 불변형(튜플)은 기능제한이 있는데, 가변형(리스트)는 기능이 풍부하다. 대신에, 가변형은 느리고 무겁다.

  • 기능적으로만 보면, 신라면이 있으면 스프를 버리고 라면사리를 얻을 수 있음, 그래서 라면사리는 필요없음.
  • 그런데 신라면에서 스프를 버려서 라면사리를 항상 얻는다면, 비효율적임.
  • 우리가 원하는게 (1) 원소를 벡터형태로 모은뒤 (2) 벡터자체를 출력해보고 (3) 각 원소를 sort하고 (4) 원소를 추가 append하고 (5) 원소를 삭제 remove 하는 것 일수도 있음. 그렇지만 진짜 단순하게 (1),(2) 만 원할수도있음.
  • 단순히 (1)-(2)의 목적으로 리스트를 사용하는건 비효율적이니까 불변형인 튜플을 만듦. (1)-(2)를 사용함에 있어서 편리성을 극대화 하기위해 괄호도 생략하게 해줌.
def mycal(a,b):
    return a+b, a-b, a*b, a/b # 여러개의 값을 리턴하는듯 보임 -> 사실은 길이가 4인 튜플 1개를 리턴
mycal(1,2)
(3, -1, 2, 0.5)
_,_,mul,_ = mycal(1,2)
mul
2

G. 연습문제들

# 문제. 길이가 1인 튜플을 만들어 자신의 학번을 저장하라. 길이가 1인 튜플을 만들어 자신의 영문이름을 저장하라. 두 튜플을 + 연산자로 합쳐아래와 같은 출력결과를 얻어라. 최종 결과는 예를들면 아래와 같아야 한다.

('2021-43052', 'GuebinChoi')
('2021-43052', 'GuebinChoi')

(풀이)

sid = ('2021-43052',)
name = ('GuebinChoi',)
sid+name
('2021-43052', 'GuebinChoi')

파이썬프로그래밍 수강생. 아래는 파이썬프로그래밍 수강생들의 학번, 이름, 출석점수, 과제점수, 중간고사점수, 기말고사점수를 저장한 중첩리스트이다.

lst = [['2021-43052', 'GuebinChoi', 5, 10, 20, 25],
       ['2019-12342', 'Heung-min Son', 10, 15, 30, 15],
       ['2018-32234', 'hynn', 7, 20, 30, 15],
       ['2022-42323', 'Minji', 8, 20, 20, 35],
       ['2023-55342', 'Hanni', 7, 20, 30, 35],
       ['2022-46624', 'Danielle', 3, 15, 30, 40],
       ['2022-11239', 'Haerin', 10, 20, 30, 40],
       ['2022-32114', 'Hyein', 10, 20, 20, 35]]
lst 
[['2021-43052', 'GuebinChoi', 5, 10, 20, 25],
 ['2019-12342', 'Heung-min Son', 10, 15, 30, 15],
 ['2018-32234', 'hynn', 7, 20, 30, 15],
 ['2022-42323', 'Minji', 8, 20, 20, 35],
 ['2023-55342', 'Hanni', 7, 20, 30, 35],
 ['2022-46624', 'Danielle', 3, 15, 30, 40],
 ['2022-11239', 'Haerin', 10, 20, 30, 40],
 ['2022-32114', 'Hyein', 10, 20, 20, 35]]

# 문제. 파이썬프로그래밍 수강생의 수는 모두 몇명인가?

(풀이)

len(lst)
8

#. 전북대학교 지침에 따라 출석점수가 7보다 작은 학생은 (즉 출석점수 < 7 일 경우) F학점을 부여하게 되어있다. 이 기준에 따르면 F를 받는 학생은 모두 몇명인가?

(풀이)

sum([att<7 for _,_,att,*other in lst])
2

# 문제. 파이썬프로그래밍 수업의 경우 출석+레포트 < 21 일 경우 F학점을 부여한다고 한다. 이 기준에 따르면 F를 받는 학생은 모두 몇명인가?

(풀이)

sum([att+rep<21 for _,_,att,rep,*other in lst])
2

# 문제. 리스트의 정렬순서를 [학번, 이름, …, 기말고사점수] 가 아니라 [이름, 학번, … , 기말고사점수] 와 같이 되도록 변경하는 코드를 작성하라.

(출력예시)

[['GuebinChoi', '2021-43052', 5, 10, 20, 25],
 ['Heung-min Son', '2019-12342', 10, 15, 30, 15],
 ['hynn', '2018-32234', 7, 20, 30, 15],
 ['Minji', '2022-42323', 8, 20, 20, 35],
 ['Hanni', '2023-55342', 7, 20, 30, 35],
 ['Danielle', '2022-46624', 3, 15, 30, 40],
 ['Haerin', '2022-11239', 10, 20, 30, 40],
 ['Hyein', '2022-32114', 10, 20, 20, 35]]
[['GuebinChoi', '2021-43052', 5, 10, 20, 25],
 ['Heung-min Son', '2019-12342', 10, 15, 30, 15],
 ['hynn', '2018-32234', 7, 20, 30, 15],
 ['Minji', '2022-42323', 8, 20, 20, 35],
 ['Hanni', '2023-55342', 7, 20, 30, 35],
 ['Danielle', '2022-46624', 3, 15, 30, 40],
 ['Haerin', '2022-11239', 10, 20, 30, 40],
 ['Hyein', '2022-32114', 10, 20, 20, 35]]

(풀이)

[[name,sid,*other] for sid,name,*other in lst]
[['GuebinChoi', '2021-43052', 5, 10, 20, 25],
 ['Heung-min Son', '2019-12342', 10, 15, 30, 15],
 ['hynn', '2018-32234', 7, 20, 30, 15],
 ['Minji', '2022-42323', 8, 20, 20, 35],
 ['Hanni', '2023-55342', 7, 20, 30, 35],
 ['Danielle', '2022-46624', 3, 15, 30, 40],
 ['Haerin', '2022-11239', 10, 20, 30, 40],
 ['Hyein', '2022-32114', 10, 20, 20, 35]]

4. 잡기술 (하지만 유용해)

A. 인덱싱고급 (스트라이딩)

- 스트라이딩 [start:end:step]

lst = list('abcdefghijk')
lst
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
len(lst)
11
lst[0:9:2]
['a', 'c', 'e', 'g', 'i']

- 생략

lst[0:9]
#lst[0:9:]
#lst[0:9:1]
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
lst[1::3]
['b', 'e', 'h', 'k']
lst[:8:3]
['a', 'd', 'g']

# 예제1: 짝수/홀수 원소 추출

아래와 같은 문자열이 있다고 하자.

lst = list('abcdefghijk')
lst
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']

index = 0,2,4, ... 에 해당하는 원소를 출력하라.

lst[::2]
['a', 'c', 'e', 'g', 'i', 'k']

index = 1,4,7 ... 에 해당하는 원소를 출력하라.

lst[1::3]
['b', 'e', 'h', 'k']

#

# 예제2 – 세로로..


(예제2를 위한 예비학습) 문자열에서 \n을 출력하면 출력시 줄바꿈이 일어난다.

print('1행\n2행\n3행')
1행
2행
3행

예비학습 끝


아래와 같은 문자열이 있다고 하자.

txt = '너같이사랑스럽고\n또예쁘고도멋지고\n속훤히보이는너알\n았어그동안고마웠\n지정말정말사랑해'
print(txt)
너같이사랑스럽고
또예쁘고도멋지고
속훤히보이는너알
았어그동안고마웠
지정말정말사랑해

위 문자열을 세로로 읽는 코드를 작성하라. (9칸씩 점프하면서 읽으면 된다)

(풀이)

txt[::9]
'너또속았지'

#

- step = -1 이면?

lst
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
lst[::-1]
['k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a']

- 스트라이딩으로 step = -1 옵션 주기 vs 리스트의 .reverse() 메소드 이용하기

관찰1: reverse 메소드는 리스트 자체를 변화시킴

lst = list('abcdefgh')
lst
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
lst.reverse()  
lst
['h', 'g', 'f', 'e', 'd', 'c', 'b', 'a']

관찰2: [::-1]는 리스트는 변화시키지 않음

lst = list('abcdefgh')
lst
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
lst[::-1]
['h', 'g', 'f', 'e', 'd', 'c', 'b', 'a']
lst
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']

- -step 은 쓰기 까다롭다.

(예제) 처음과 끝을 생략하지 않고 아래와 동일한 효과를 주는 코드를 만들어 보자.

(풀이)

결국 lst[?:?:-1]의 꼴에서 적당히 ?의 값을 채우면 된다. –> 어려워

lst
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
lst[::-1]
['h', 'g', 'f', 'e', 'd', 'c', 'b', 'a']
None a b c d e f g h None
? 0 1 2 3 4 5 6 7 8
-9 -8 -7 -6 -5 -4 -3 -2 -1 ?
lst[-1:-9:-1]  # 지양 <-- 쓰지마..
['h', 'g', 'f', 'e', 'd', 'c', 'b', 'a']

B. len함수

- 0차원 자료형은 len함수가 동작하지 않음

a=1 
len(a)
TypeError: object of type 'int' has no len()
a=True
len(a)
TypeError: object of type 'bool' has no len()
a=3.14
len(a)
TypeError: object of type 'float' has no len()

note: 이것이 어떠한 수학적인 의미를 가지거나 0차원의 본질적진리를 뜻하는 것은 아님. R에서는 1,3.14,TRUE의 길이가 1로 존재함.

- 1차원 자료형은 len함수가 동작

a='guebin'
len(a)
6
a=[1,2,3,4,5,6]
len(a)
6
a=1,2,3,4,5,6 
len(a)
6
a=range(10)
len(a)
10

- 길이가 1인 1차원 자료형과 0차원 자료형은 다른것임

a='g'
len(a)
1
a=[1]
len(a)
1
a=(1,)
len(a)
1
a=range(1)
len(a)
1

- 길이가 0인 1차원 자료형도 존재함

a=''
len(a)
0
a=[]
len(a)
0
a=()
len(a)
0
a=range(0)
len(a)
0

5. dict

A. intro: str, list, tuple 정리

- str, list, tuple은 모두 시퀀스형이라는 공통점이 있다. \(\to\) 원소의 위치번호로 인덱싱이 가능

lst = [1,2,3,4]
lst[0] # 위치번호=0
1
lst[-1] # 위치번호=-1
4

- str, list, tuple은 차이점도 존재함. 잠깐 정리해보자.

시퀀스형의 카테고리

  • 컨테니어형: list, tuple
  • 균일형: str
  • 가변형: list
  • 불변형: tuple, str

표로 정리하면

컨테니어형 균일형
가변형 list .
불변형 tuple str

- 시퀀스형이 아닌 1차원 자료형도 있을까? 원소의 위치번호로 인덱싱이 불가능한 자료형

- 왜 이런게 필요할까?

  • 벡터에서 원소를 뽑는것은 정보의 모임에서 정보를 검색하는 것과 같다.
  • 정보를 순서대로 나열한뒤에 그 순서를 이용하여 검색하는 방법은 유용하다.
  • 하지만 경우에 따라서는 키워드를 기억해서 그 키워드를 바탕으로 정보에 접근하는 방법이 유용할 수 있다.

카카오톡 대화내용검색

(상황1) 오늘아침에 와이프가 뭔가를 카톡으로 부탁했었음. 그런데 그 뭔가가 기억안남.

(상황2) 방학전에 동료교수과 개강이후 저녁약속을 카톡으로 잡았었음. 그런데 그게 언제인지 기억안남.

- 순서대로 정리된 자료를 검색할때는 시퀀스형이 유리하다. 그런데 키워드로 검색하고 싶을 경우는 딕셔너리 타입이 유리하다.

B. 선언

- 방법1: 가장 일반적

dct = {'guebin':49, 'hanni':80}
dct
{'guebin': 49, 'hanni': 80}

- 방법2: dict() 이용

dct = dict(guebin=49, hanni=80)
dct
{'guebin': 49, 'hanni': 80}

- 방법3: 중첩된 리스트를 만든 뒤에 형태변환

_lst = [['guebin',49],['hanni',80]]
_lst 
[['guebin', 49], ['hanni', 80]]
dict(_lst)
{'guebin': 49, 'hanni': 80}

- 방법4: 중첩된 튜플을 만든 뒤에 형태변환

_tpl = ('guebin',49), ('hanni',80)
_tpl
(('guebin', 49), ('hanni', 80))
dict(_tpl)
{'guebin': 49, 'hanni': 80}

C. 원소추출

- 원소의 위치로 추출할 수 없고, key로 추출해야 한다.

dct = {'guebin':49, 'hanni':80}
dct
{'guebin': 49, 'hanni': 80}

guebin의 점수를 추출하고 싶다면?

dct['guebin']
49

- 만약에 dict가 아니라 list로 정보를 저장했다면?

(예제) 아래와 같은 리스트에서 guebin의 점수를 추출하고 싶다면?

lst=[['guebin',49],['hanni',80]]
lst
[['guebin', 49], ['hanni', 80]]

(풀이1)

lst[0][1] # guebin의 점수를 출력하란 의미
49

(풀이2) – 진짜 최악

[lst[i][1] for i in range(len(lst)) if lst[i][0] == 'guebin']
[49]

(풀이3) – 덜 최악

[score for name,score in lst if name == 'guebin']
[49]

- ’guebin’의 점수를 추출하는 코드 비교

dct['guebin'] # 코드1: 단순하고, 가독성있음
49
lst[0][1] # 코드2: 단순하지만, 가독성이 있는건 아님, 확장성이 없음
49
[lst[i][1] for i in range(len(lst)) if lst[i][0] =='guebin'] # 코드3: 단순하지도 않고, 가독성도 없음.
[49]
[score for name,score in lst if name=='guebin' ] # 코드4: 단순하지 않지만, 가독성은 있음
[49]

D. 원소추가, 변경, 삭제

dct={'guebin':49, 'hanni':80}
dct
{'guebin': 49, 'hanni': 80}

- 원소에 접근: guebin의 점수 출력

dct['guebin']
49

- 추가: hynn학생의 점수를 추가

dct['hynn'] = 99
dct
{'guebin': 49, 'hanni': 80, 'hynn': 99}

- 변경: hanni의 점수를 변경

dct['hanni'] = 100 
dct
{'guebin': 49, 'hanni': 100, 'hynn': 99}

- 삭제

(방법1)

dct={'guebin':49, 'hanni':80, 'hynn':99}
del dct['guebin']  
dct
{'hanni': 80, 'hynn': 99}

(방법2)

dct={'guebin':49, 'hanni':80, 'hynn':99} 
dct.pop('guebin')
49
dct
{'hanni': 80, 'hynn': 99}

- 참고로 리스트였다면 이러한 삭제작업역시 비효율적이었을 것임

lst = [['guebin',49],['hanni',80],['hynn',99]] 
lst
[['guebin', 49], ['hanni', 80], ['hynn', 99]]

guebin의 점수를 삭제하려면?

[[name,score] for name,score in lst if name != 'guebin']
[['hanni', 80], ['hynn', 99]]

E. 연산

- 하나있어요..

dct = {'guebin':49, 'hanni':80} 
dct
{'guebin': 49, 'hanni': 80}
'guebin' in dct
True
'hanni' in dct
True
'hynn' in dct
False

- in은 사실 다른자료형도 가능했음

(관찰1)

'a' in 'guebin' 
False
'b' in 'guebin' 
True
'c' in 'guebin' 
False

(관찰2)

tpl = 1,2,3 
tpl
(1, 2, 3)
1 in tpl
True
4 in tpl
False

(관찰3)

lst = [['guebin',49],['hanni',80],['hynn',99]] 
lst
[['guebin', 49], ['hanni', 80], ['hynn', 99]]
['guebin',49] in lst
True

- in연산자가 dict형에 사용되면 key를 기준으로 True, False를 판단한다.

F. 딕셔너리 특수기능

(pop)

dct = {'guebin':49, 'hanni':80} 
dct.pop('hanni')
dct
{'guebin': 49}

(get)

dct = {'guebin':49, 'hanni':80} 
dct
{'guebin': 49, 'hanni': 80}
dct.get('guebin') 
49

아래와 같은 기능

dct['guebin']
49

미묘한 차이점이 존재함

dct['hynn'] # hynn이 없어서 키에러 출력, 그런 key는 없다.. 
KeyError: 'hynn'
dct.get('hynn') # hynn이 없으면 아무것도 출력안함 

(keys,values,items)

for k,v in dct.items():
    print(k,v)
guebin 49
hanni 80

- .keys()는 딕셔너리의 키를 리턴한다.

dct = {'guebin':49, 'hanni':80} 
dct
{'guebin': 49, 'hanni': 80}
_keys=dct.keys()
_keys
dict_keys(['guebin', 'hanni'])
type(_keys) # 리턴된 자료형은 이상한것임
dict_keys
list(_keys) # 아무튼 그 이상한 자료형도 리스트화 가능 
['guebin', 'hanni']

- .values()는 딕셔너리의 값들을 리턴한다.

_values = dct.values()
_values 
dict_values([49, 80])
type(_values)
dict_values
list(_values)
[49, 80]

- .items()는 딕셔너리의 (키,값)을 리턴한다.

_items = dct.items()
_items 
dict_items([('guebin', 49), ('hanni', 80)])
type(_items)
dict_items
list(_items)
[('guebin', 49), ('hanni', 80)]

G. for문과 dict (\(\star\))

dct = {'guebin': 49, 'hanni': 80}
dct
{'guebin': 49, 'hanni': 80}

(예시1)

for k in dct.keys():
    print(k)
guebin
hanni
for k in dct:
    print(k)
guebin
hanni
  • 딕셔너리 그자체도 for문에 넣을 수 있다. 그때는 딕셔너리의 key가 반복된다.
  • 결과를 보면 dct 대신에 dct.keys()와 list(dct)를 넣었을때와 결과가 같다.

Note: list(dct) 하면 key만 리턴된다.

(예시2)

for v in dct.values():
    print(v)
49
80

(예시3)

for i in dct.items():
    print(i)
('guebin', 49)
('hanni', 80)

(예시4)

for k,v in dct.items():
    print(k,v)
guebin 49
hanni 80

(예시5) – {}의 중간고사 점수는 {}점 입니다.

for name,score in dct.items():
    print(f'{name}의 중간고사 점수는 {score}점 입니다.')
guebin의 중간고사 점수는 49점 입니다.
hanni의 중간고사 점수는 80점 입니다.

H. dict에서 key혹은 value만 뽑아내기

- 예제: 아래의 dict에서 key만 뽑아내고 싶다.

dct = {'guebin':49, 'hanni':80} 

(풀이1)

list(dct)
['guebin', 'hanni']

(풀이2)

list(dct.keys())
['guebin', 'hanni']

(풀이3)

[k for k in dct]
['guebin', 'hanni']

(풀이4)

[k for k,v in dct.items()]
['guebin', 'hanni']

- 예제: 아래의 dict에서 value만 뽑아내고 싶다.

dct = {'guebin':49, 'hanni':80} 

(풀이1)

list(dct.values())
[49, 80]

(풀이2)

[dct[k] for k in dct]
[49, 80]

(풀이3)

[v for v in dct.values()]
[49, 80]

(풀이4)

[v for k,v in dct.items()]
[49, 80]

6. 딕셔너리를 이용한 치환 (\(\star\))

A. 바꿔치기

# 예제1 – 아래와 같은 리스트를 고려하자.

lst = ['딸기','사과','바나나','딸기','사과','오토바이','자동차','버스','기차','오토바이','자동차']

다음의 맵핑규칙에 따라서 위의 리스트의 원소를 바꾸어라.

변환전 변환후
딸기 과일
사과 과일
바나나 과일
오토바이 탈것
자동차 탈것
버스 탈것
기차 탈것

(풀이1)elif를 한번 써본정도의 의미. 추천하는 풀이 X

lst2 = []
for l in lst:
    if l=='딸기':
        lst2.append('과일')
    elif l=='사과':
        lst2.append('과일')
    elif l=='바나나':
        lst2.append('과일')
    elif l=='오토바이':
        lst2.append('탈것')
    elif l=='자동차':
        lst2.append('탈것')
    elif l=='버스':
        lst2.append('탈것')
    else:
        lst2.append('탈것')        
lst,lst2
(['딸기', '사과', '바나나', '딸기', '사과', '오토바이', '자동차', '버스', '기차', '오토바이', '자동차'],
 ['과일', '과일', '과일', '과일', '과일', '탈것', '탈것', '탈것', '탈것', '탈것', '탈것'])

(풀이2) – 코드는 쉽고 편함. dct를 만들기 힘듦.

dct = {'딸기':'과일','사과':'과일','바나나':'과일','오토바이':'탈것','자동차':'탈것','버스':'탈것','기차':'탈것'}
dct
{'딸기': '과일',
 '사과': '과일',
 '바나나': '과일',
 '오토바이': '탈것',
 '자동차': '탈것',
 '버스': '탈것',
 '기차': '탈것'}
[dct[l] for l in lst]
['과일', '과일', '과일', '과일', '과일', '탈것', '탈것', '탈것', '탈것', '탈것', '탈것']

(풀이3) – 코드가 매우 어려움. dct를 만들기 쉬움.

dct = {'과일':['딸기','사과','바나나'], '탈것':['오토바이','자동차','버스','기차']}
dct
{'과일': ['딸기', '사과', '바나나'], '탈것': ['오토바이', '자동차', '버스', '기차']}
lst
['딸기', '사과', '바나나', '딸기', '사과', '오토바이', '자동차', '버스', '기차', '오토바이', '자동차']
[k for l in lst for k in dct if l in dct[k]]
['과일', '과일', '과일', '과일', '과일', '탈것', '탈것', '탈것', '탈것', '탈것', '탈것']

#

# 예제2 – 아래와 같은 리스트가 있다고 하자.

lst = list('abcd'*2+'bbb')
lst
['a', 'b', 'c', 'd', 'a', 'b', 'c', 'd', 'b', 'b', 'b']

아래의 규칙에 의하여 lst의 각 원소의 값을 바꾸고 싶다고 하자. 이를 구현하는 코드를 작성하라.

변환전 변환후
‘a’ [1,0,0,0]
‘b’ [0,1,0,0]
‘c’ [0,0,1,0]
‘d’ [0,0,0,1]

(풀이)

dct = {'a':[1,0,0,0], 'b':[0,1,0,0], 'c':[0,0,1,0], 'd':[0,0,0,1]}
dct
{'a': [1, 0, 0, 0], 'b': [0, 1, 0, 0], 'c': [0, 0, 1, 0], 'd': [0, 0, 0, 1]}
[dct[l] for l in lst]
[[1, 0, 0, 0],
 [0, 1, 0, 0],
 [0, 0, 1, 0],
 [0, 0, 0, 1],
 [1, 0, 0, 0],
 [0, 1, 0, 0],
 [0, 0, 1, 0],
 [0, 0, 0, 1],
 [0, 1, 0, 0],
 [0, 1, 0, 0],
 [0, 1, 0, 0]]

# 예제3 – 예제2을 역변환하라. 즉 아래의 리스트를

lst2= [[1, 0, 0, 0],
       [0, 1, 0, 0],
       [0, 0, 1, 0],
       [0, 0, 0, 1],
       [1, 0, 0, 0],
       [0, 1, 0, 0],
       [0, 0, 1, 0],
       [0, 0, 0, 1],
       [0, 1, 0, 0],
       [0, 1, 0, 0],
       [0, 1, 0, 0]]

아래와 같이 바꾸라.

['a', 'b', 'c', 'd', 'a', 'b', 'c', 'd', 'b', 'b', 'b']
['a', 'b', 'c', 'd', 'a', 'b', 'c', 'd', 'b', 'b', 'b']

(풀이) – 실패

dct = {[1,0,0,0]:'a', [0,1,0,0]:'b', [0,0,1,0]:'c', [0,0,0,1]:'d'}
dct
TypeError: unhashable type: 'list'

dct의 key에 대응하는것은 불변형만 가능

(풀이1)

dct = {(1,0,0,0):'a', (0,1,0,0):'b', (0,0,1,0):'c', (0,0,0,1):'d'}
dct
{(1, 0, 0, 0): 'a', (0, 1, 0, 0): 'b', (0, 0, 1, 0): 'c', (0, 0, 0, 1): 'd'}
[dct[tuple(l)] for l in lst2]
['a', 'b', 'c', 'd', 'a', 'b', 'c', 'd', 'b', 'b', 'b']

(풀이2)

dct = {'a':[1,0,0,0], 'b':[0,1,0,0], 'c':[0,0,1,0], 'd':[0,0,0,1]} # 예제2의 dct
[k for l in lst2 for k in dct if dct[k]==l]
['a', 'b', 'c', 'd', 'a', 'b', 'c', 'd', 'b', 'b', 'b']

B. 연습문제

# 문제. 아래와 같은 맵핑을 고려하자.

문자 숫자
a 0
b 1

이를 딕셔너리로 표현하면 아래와 같다.

dct = {'a':0, 'b':1} 

위 규칙에 따라서 아래의 리스트의 원소를 문자로 각각 변환하라.

lst = [1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1]
# 출력은 아래와 같아야 한다. 
['b', 'a', 'b', 'a', 'b', 'a', 'b', 'b', 'b', 'b', 'a', 'a', 'b', 'a', 'b']

(풀이)

[k for l in lst for k in dct if l == dct[k]]
['b', 'a', 'b', 'a', 'b', 'a', 'b', 'b', 'b', 'b', 'a', 'a', 'b', 'a', 'b']

# 문제. 아래와 같은 맵핑을 고려하자.

의미
1,2 겨울방학
3,4,5,6 1학기
7,8 여름방학
9,10,11,12 2학기

이러한 규칙에 맞게 아래의 리스트를 적절한 문자열로 변환하라.

month = [1,2,2,3,4,5,6,7,8,9,9,10,11,12] 
## 출력은 아래와 같아야 한다. 
['겨울방학', '겨울방학', '겨울방학', '1학기', '1학기', '1학기', '1학기', '여름방학', '여름방학', '2학기', '2학기', '2학기', '2학기', '2학기']

(풀이1) – 좀 더 추천함

dct = {'겨울방학':range(1,3), '1학기':range(3,7), '여름방학':range(7,9), '2학기':range(9,13)}
[k for m in month for k in dct if m in dct[k]]
['겨울방학',
 '겨울방학',
 '겨울방학',
 '1학기',
 '1학기',
 '1학기',
 '1학기',
 '여름방학',
 '여름방학',
 '2학기',
 '2학기',
 '2학기',
 '2학기',
 '2학기']

(풀이2) – 추천안함

dct = {1:'겨울방학',2:'겨울방학',3:'1학기',4:'1학기',5:'1학기',6:'1학기',7:'여름방학',8:'여름방학',9:'2학기',10:'2학기',11:'2학기',12:'2학기'}
[dct[m] for m in month]
['겨울방학',
 '겨울방학',
 '겨울방학',
 '1학기',
 '1학기',
 '1학기',
 '1학기',
 '여름방학',
 '여름방학',
 '2학기',
 '2학기',
 '2학기',
 '2학기',
 '2학기']

## – 합성변환

아래와 같은 맵핑을 고려하자.

(규칙1)

문자 숫자
바나나 0
사과 1
오토바이 2
자동차 3
자전거 4

(규칙2)

아이템 카테고리
바나나 과일
사과 과일
오토바이 탈것
자동차 탈것
자전거 탈것

각각의 규칙을 나타내는 딕셔너리는 아래와 같이 선언되어있다고 하자.

dct1 = {'바나나':0, '사과':1, '오토바이':2, '자동차':3, '자전거':4} 
dct2 = {'과일':['바나나','사과'], '탈것':['오토바이','자동차','자전거']} 

# 문제. 규칙1를 이용하여 아래와 같은 리스트를 변환하는 함수를 구현하고 그 함수를 f라 선언하라.

# 입력 
[0,1,0,1,4]

# 출력 
['바나나', '사과', '바나나', '사과', '자전거']

(사용예시)

f([0,1,0,1,4])
['바나나', '사과', '바나나', '사과', '자전거']

(풀이)

f = lambda lst: [k for l in lst for k in dct1 if l == dct1[k]]
f([0,1,0,1,4])
['바나나', '사과', '바나나', '사과', '자전거']

# 문제. 규칙2를 이용하여 아래와 같이 리스트를 변환하는 함수를 구현하고 그 함수를 g라고 선언하라.

# 입력 
['바나나','바나나','바나나','자동차']

# 출력 
['과일','과일','과일','탈것']

(사용예시)

g(['바나나','바나나','바나나','자동차'])
['과일', '과일', '과일', '탈것']

(풀이)

g = lambda lst: [k for l in lst for k in dct2 if l in dct2[k]]
g(['바나나','바나나','바나나','자동차'])
['과일', '과일', '과일', '탈것']

# 문제. 규칙1-2를 이용하여 아래와 같은 숫자로 이루어진 입력을 ‘과일’, ‘탈것’ 중 하나로 바꾸는 코드를 구현하라.

# 입력 
[0,1,0,1,3,4,2,2,3,4,1,0]

# 출력 
['과일', '과일', '과일', '과일', '탈것', '탈것', '탈것', '탈것', '탈것', '탈것', '과일', '과일']

hint \(g(f(x))\) 를 이용하라.

(풀이)

g(f([0,1,0,1,3,4,2,2,3,4,1,0]))
['과일', '과일', '과일', '과일', '탈것', '탈것', '탈것', '탈것', '탈것', '탈것', '과일', '과일']

7. 파이썬 자료형 연습문제

Oxford-III: // reference

아래는 이미지 파일명들이 저장된 string을 불러오는 코드이다.

import requests
url = 'https://raw.githubusercontent.com/guebin/PP2023/main/posts/01_PythonBasic/Oxford-IIIT.txt'
txt = requests.get(url).content.decode()

txt의 출력 일부를 나타내면 아래와 같다.

'Abyssinian_1.jpg\nAbyssinian_10.jpg\nAbyssinian_100.jpg\nAbyssinian_100.mat\nAbyssinian_101.jpg\nAbyssinian_101.mat\nAbyssinian_102.jpg\nAbyssinian_102.mat\nAbyssinian_103.jpg\nAbyssinian_104.jpg\nAbyssinian_105.jpg\nAbyssinian_106.jpg\nAbyssinian_107.jpg\nAbyssinian_108.jpg\nAbyssinian_109.jpg\nAbyssinian_11.jpg\nAbyssinian_110.jpg\nAbyssinian_111.jpg\nAbyssinian_112.jpg\nAbyssinian_113.jpg\nAbyssinian_114.jpg\nAbyssinian_115.jpg\nAbyssinian_116.jpg\nAbyssinian_117.jpg\nAbyssinian_118.jpg\nAbyssinian_119.jpg\nAbyssinian_12.jpg\nAbyssinian_120.jpg\nAbyssinian_121.jpg\nAbyssinian_122.jpg\nAbyssinian_123.jpg\nAbyssinian_124.jpg\nAbyssinian_125.jpg\nAbyssinian_126.jpg\nAbyssinian_127.jpg\nAbyssinian_128.jpg\nAbyssinian_129.jpg\nAbyssinian_13.jpg\nAbyssinian_130.jpg\nAbyssinian_131.jpg\nAbyssinian_132.jpg\n ....... 

# 문제. 각 파일명은 \n으로 구분되어있다. 위의 스트링을 분해하여 아래와 같은 리스트를 생성하고 fname_list에 저장하라.

# fname_list 의 출력결과는 아래와 같아야 한다. 
['Abyssinian_1.jpg','Abyssinian_10.jpg', ... ,'yorkshire_terrier_98.jpg', 'yorkshire_terrier_99.jpg']

(풀이)

fname_list = txt.split('\n')
fname_list[:5] # 7393개의 이미지파일명이 저장되어있음. 
['Abyssinian_1.jpg',
 'Abyssinian_10.jpg',
 'Abyssinian_100.jpg',
 'Abyssinian_100.mat',
 'Abyssinian_101.jpg']

# 문제. 각 이미지파일명은 아래와 같은 규칙으로 저장되어 있다.

  • 파일명의 첫글자가 대문자이면 고양이를 의미하고 첫글자가 소문자이면 강아지를 의미한다.

이미지 파일명이 입력으로 오면 강아지인지 고양이인지 판단하여 ‘cat’ or ’dog’를 리턴하는 함수 f를 구현하라.

(함수사용예시)

f('yorkshire_terrier_99.jpg')
'dog'
f('Abyssinian_1.jpg')
'cat'

(풀이)

f = lambda s: 'cat' if s[0].isupper() else 'dog'
f('Abyssinian_1.jpg')
'cat'

# 문제. 위의 결과로 나온 fname_list를 입력으로 하고 리스트의 각 원소가 고양이를 의미하는 그림인지 강아지를 의미하는 그림인지 나타내는 리스트를 만들어라.

## 입력예시 
['Abyssinian_1.jpg','Abyssinian_10.jpg',...,'yorkshire_terrier_98.jpg', 'yorkshire_terrier_99.jpg']

## 출력예시
['cat', 'cat', ... , 'dog', 'dog']

(풀이1)

#list(map(f,fname_list))

(풀이2)

#[f(fname) for fname in fname_list]

# 문제. 강아지 그림과 고양이 그림이 각각 몇 장씩 포함되어 있는지 파악하는 코드를 구현하라.

(풀이)

[f(fname) for fname in fname_list].count('cat')
2403
[f(fname) for fname in fname_list].count('dog')
4990

# 문제. 고양이 혹은 강아지의 각 종이 몇 장씩 포함되어있는지 구하여 딕셔너리로 정리하라.

(풀이)

lst = ['_'.join(l.split('_')[:-1]) for l in fname_list]
{s:lst.count(s) for s in set(lst)}
{'yorkshire_terrier': 200,
 'Siamese': 200,
 'chihuahua': 200,
 'Persian': 200,
 'miniature_pinscher': 200,
 'samoyed': 200,
 'boxer': 200,
 'american_pit_bull_terrier': 200,
 'german_shorthaired': 200,
 'great_pyrenees': 200,
 'Sphynx': 200,
 'shiba_inu': 200,
 'american_bulldog': 200,
 'havanese': 200,
 'Bombay': 200,
 'japanese_chin': 200,
 'Ragdoll': 200,
 'english_cocker_spaniel': 200,
 'english_setter': 200,
 'Egyptian_Mau': 200,
 'staffordshire_bull_terrier': 191,
 'Russian_Blue': 200,
 'beagle': 200,
 'pomeranian': 200,
 'British_Shorthair': 200,
 'pug': 200,
 'Bengal': 200,
 'newfoundland': 200,
 'wheaten_terrier': 200,
 'Abyssinian': 203,
 'Maine_Coon': 200,
 'basset_hound': 200,
 'saint_bernard': 200,
 'scottish_terrier': 199,
 'keeshond': 200,
 'leonberger': 200,
 'Birman': 200}