Eat Study Love

먹고 공부하고 사랑하라

SW 만학도/Python

Review 3 - Loop & Set,Tuple,Dictionaries,Mutability

eatplaylove 2024. 7. 2. 14:08

https://eglife.tistory.com/66

 

Review 2 - Python의 기본 Module , Class

https://eglife.tistory.com/65 Review 1 - Python programming basics프로그래밍은 안 쓰다 보면 까먹는다. 더군다나 전공자가 아니면 정말 1~2주만 정신 놓고 있다가 복귀했을 때 굉장히 기초적인 코딩도 손에 잡

eglife.tistory.com

Module을 통으로 import를 하나, 그 중에 일부만 import를 하나 memory 측면에선 전혀 차이가 없다만,

Global namespace에서 어떤 이름이 올라와 있냐의 차이만 있었다.

---> math가 올라와 있어서 math.sqrt 등 dot을 통해 접근하냐?

vs from math import sqrt 해서 sqrt가 떡하니 global에 올라와 있냐의 차이!를 알자

 

요번엔 List , Loop 등 슬슬 Python의 메인 영역으로 들어가보자. 복기 시작!

 

1. List

 

Data들은 보통 그룹화가 되어있다. 이제 슬슬 python으로 group화된 Data를 다루는 법을 배워봅시다.

 

- List는 순서가 있다. (Ordered O)

- 대괄호로 표현하며, 다양한 method가 있다.

 

students 라는 list가 있다고 할 때, Memory 할당은 index별로 요렇게 할당된다.

 

list 안에 element를 바꿔줄 땐, 기존 "yesong" box의 value가 바뀌는 것이 아니라 "kakyeong"이라는 Memory가 생기고, students[1] 이 그것을 pointing 하면서 Data 수정이 일어난다.

 

※ List 안에 저장되는 element 들의 Data type(boolean, int ..)은 다양하게 저장할 수도 있지만 권장X!

※ C, C++에선 List(Container)에 여러 가지 data type을 아~예 넣질 못함!

 

from typing import List
def avg(L:list[float]) -> float:
    return L[0]
avg([1,2,3])

<권장 표시법>

요런식으로 표현도 가능하다

 

다양한 list의 operator들

 

위와 같이 Operator의 경우 A 자체만 가지고 놀지만 아래 Method의 경우 Parameter가 필요할 때, Parameter를 적어준다.

 

 

List에서 특정 부분만 필요할 때는 Slice해서 쓴

 

사실 Slicing 부분이 Python의 가장 큰 매력이지 않을까 싶다. C / C++ 하다 보면 이 Slicing이 너무나 그립다..

 

2. List - Copy and Alias ( 쌍둥이냐=copy 별명=alias냐의 차이 )

 

Copy가 죽으면 나는 살지만, Alias가 죽으면 나도 죽는다..

 

# 요것은 Copy의 일종
A_copy = A[:]
print(A_copy)
A_copy[0] = 1234
print(A_copy)
print(A)
[999, 100, 8, 7, 6, 1, 0, -4, -123]
[1234, 100, 8, 7, 6, 1, 0, -4, -123]
[999, 100, 8, 7, 6, 1, 0, -4, -123]

# 요것은 Alias 일종
A_copy = A
print(A_copy)
A_copy[0] = 1234
print(A_copy)
print(A)
[999, 100, 8, 7, 6, 1, 0, -4, -123]
[1234, 100, 8, 7, 6, 1, 0, -4, -123]
[1234, 100, 8, 7, 6, 1, 0, -4, -123]

 

# Alias 여도 삭제한다고 원본이 삭제되지 않는다. 수정만 째깍째깍 반영되고
A=[1, 8, 100, -123, 5, 6, 7, 0, -4]
A_copy = A
print(A_copy)
A_copy[0] = 1234
print(A_copy)
del A_copy
print(A)
print(A_copy)
​
[1, 8, 100, -123, 5, 6, 7, 0, -4]
[1234, 8, 100, -123, 5, 6, 7, 0, -4]
[1234, 8, 100, -123, 5, 6, 7, 0, -4]
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[137], line 8
      6 del A_copy
      7 print(A)
----> 8 print(A_copy)

NameError: name 'A_copy' is not defined

 

함수에 Alias가 넘어가면 원본에 영향이 간

# 위 사항, 적용되는 지 혼자 실험해보기..된다
def del_last(L:list[int])->list :
    del L[-1]
test2 = [1,2,3]
del_last(test2)
print(test2)
[1, 2]

 

위 현상은 Local Variable "L"이 우리의 Global Varialbe list를 Alias로 포인팅하면서 발생한 case이다. Memory 구조는 아래와 같다.

마냥 문제라고 볼 수 없다. 이것을 일부로 노려서 코딩을 하는 경우도 있을 터이

 

즉, Slicing 을 써서 Copying을 쓰는 것이 일반적으론 안전하다.

Function argument를 넘길 때도 원본을 넘기면 alias로 넘어가니까 copy를 만들어서 넘기던지, 아니면 잘 유념하고 있던지 하자

 

3. Loops - For loop

 

Repetition을 하는 건 귀찮으니까 써주는 것이 for loop이다. 사실,, 코딩하다보면 젤 많이 쓰이는 스킬이 아닐까 싶다.

 

For Loop 역시 세미콜론으로 구분하기에 Indentation이 필수다.

#에시로 간단히 알아보는 for loop
course = ["A","B","C"]
for x in course:
    print("IN LOOP, There is : ",x)
IN LOOP, There is :  A
IN LOOP, There is :  B
IN LOOP, There is :  C

 

List가 아닌 String에서도 for loop를 돌릴 수 있다는 것 유의!

name = "Bgg-LEE"
for ch in name:
    if(ch.isupper()): #케릭터가 대문자인지 확인
        print(ch)
B
L
E
E

 

이번엔 제일 많이 쓰이는 Range of Numbers를 for loop 돌리기!

 

하나 주의하면 좋은 건, range는 숫자가 0부터 시작해서 range(N)의 경우 실제 0~N-1까지 N개의 숫자가 loop를 돈다.

 

#Case 별로 확인해보기
print("case1")
for x in range(10):
    print(x)
print("\ncase2")
for x in range(3,10):
    print(x)
print("\ncase3")
for x in range(4,10,2): # step, 간격을 2 씩 줘봤다
    print(x)
print("\ncase4")
for x in range(10,1,-2): # 뒤로 빽 하며 세는 것도 가능
    print(x)
case1
0
1
2
3
4
5
6
7
8
9

case2
3
4
5
6
7
8
9

case3
4
6
8

case4
10
8
6
4
2

 

그리고 이건 C언어의 pointer 중 call by reference, call by value와 비슷한 느낌인데,

 

for loop로 list 의 값을 바꿔주고 싶을 땐 아래와 같이 값만 바꿔주면 안 되고, index를 통해서 접근해야 한다.

 

num 이라는 것의 주소만 바뀌지, list values가 포인팅 하는 메모리주소는 변동이 없다.

 

index를 바꿔줘야 실제 list 원본이 바뀐다는 것!

 

일종의 포인터, call by reference라는 개념이 Python에선 index접근으로 이뤄진다.

 

list modifing 할 때 유의해아 하는 점이다!

values = [11,22,33]
for x in range(len(values)):
    values[x] = 2*values[x]
values
[22, 44, 66]

 

if문과 마찬가지로 for문도 nested하게 표현이 충분히 가능하다.

 

사실 이렇게 하면 나중에 나올 표현으로 Time complexity가 O(N^2)가 되어 느려지긴 하는데,

 

이걸 어떻게 줄일지는 알고리즘적으로 풀어갈 문제라 나도 더 숙달이 필요한 부분 ㅠㅠ

 

list1 = ["A","B","C"]
list2 = ["X","Y","Z"]
cnt = 1
for i in range(len(list1)):
    for j in range(len(list2)):
        print(cnt,"번째 조합",i,j)
        cnt = cnt+1
1 번째 조합 0 0
2 번째 조합 0 1
3 번째 조합 0 2
4 번째 조합 1 0
5 번째 조합 1 1
6 번째 조합 1 2
7 번째 조합 2 0
8 번째 조합 2 1
9 번째 조합 2 2

 

 

4. Loops - While loop

While Loop의 표현식은 다음과 같다.

 

설정해둔 Condition이 True라면 계속 해서 block 부분을 실행한다는 것. 이 역시 세미콜론이 쓰이는 구문이기 때문에 Indentation으로 Block 부분 구분이 필수다.

 

그리고 loop가 언젠간 멈출 수 있게 block을 finite하게 잘 설정해줘야 한다.

아니면 그 유명한 Infinite loop, 무한루프에 빠지게 된다 ㅠㅠ 이럴 땐 Ctrl + C로 프로그램 끝내고 다시 수정해야함!

 

#이런식으로 while문을 통해 user에게 data 입력받기도 가능하다.
text = ""
list1 = []
while text != "quit":
    text = input("Gimme your MBTI, 끝내고 싶으면 quit 입력:")
    if text != "quit":
        list1.append(text)
list1
Gimme your MBTI, 끝내고 싶으면 quit 입력:ENTP
Gimme your MBTI, 끝내고 싶으면 quit 입력:ISTJ
Gimme your MBTI, 끝내고 싶으면 quit 입력:ABCD
Gimme your MBTI, 끝내고 싶으면 quit 입력:quit
['ENTP', 'ISTJ', 'ABCD']

 

- Loop문 Control 하기 with Break & Continue

 

 

Break를 if문과 결합시키면 유용하게 while문을 컨트롤 할 수 있다.

 

이렇게 대문자가 언제 첫 번째로 나타나는지 체크하는 코드를 짤 수도 있고!

text = "asdAbasdqweDSVEAsdr"
# find the first upper letter in the given text
for ch in text:
    if ch.isupper():
        print("Index:",text.find(ch),",letter:",ch)
        break
Index: 3 ,letter: A

 

 

※참고로, 모든 대문자 letter에 대해서 index 중복을 피하면서 대문자의 index, 값을 출력하는 방법은 아래와 같다.

#방법 1 그냥 idx를 for문으로 돌리기
text = "asdAbasdqweDSVEAsdr"
for i in range(len(text)):
    ch = text[i]
    if ch.isupper():
        print("index :",i,",value:",ch)
index : 3 ,value: A
index : 11 ,value: D
index : 12 ,value: S
index : 13 ,value: V
index : 14 ,value: E
index : 15 ,value: A
#방법 2 enumerate 라는 것을 사용하기
text = "asdAbasdqweDSVEAsdr"
# find the first upper letter in the given text
​
for index, ch in enumerate(text):
    if ch.isupper() and index not in found_indices:
        print("Index:", index, ",letter:", ch)
Index: 3 ,letter: A
Index: 11 ,letter: D
Index: 12 ,letter: S
Index: 13 ,letter: V
Index: 14 ,letter: E
Index: 15 ,letter: A

※ enumerate(list)는 list의 index와 value를 각각 반환해주어 중복을 피할 수 있게 해준다.

 

Continue문은 아래와 같이 쓸 수 있다.

Continue를 만나면 그 순간, 그 아래 부분을 무시하고 다시 다음 loop로 올라간다.loop가 완전히 끝나는 건 for문을 다 돌거나 while문의 조건이 false가 되거나 loop가 Break를 만났을 때다.

 

5. Sets : 기본적인 골자는 중복을 무시한다. (Ordered X, Distinct)

 

Set은 중복제거 + 순서상관X 이다. 내가 정한 순서대로 값이 저장되는 것이 아니라 index를 통한 접근이 불가하다.

대신에 Mutable한 structure라서 list처럼 data 추가/제거 등 수정이 가능하다.

setA
setA = {'a','d','c','a'}
setA
{'a', 'c', 'd'}

중복 값을 왕창 넣어도 set끼리는 중복값 제거하고 같은 지 확인한다.

 

list를 set으로 만들어 줄 수도 있다. 중복은 제거되고 순서도 무의미해지긴 하지만!

listA = [3,2,4,2,5]
setA = set(listA)
setA
{2, 3, 4, 5}

 

set도 list와 같이 역시나 for문을 돌릴 수 있다. 하지만 UNORDERED 구조니까

for문이 뱉는 순서는 의미가 없고, 알 수도 없다.

setB = {1,5,2,1,5,6,7,0,-5,100}
for x in setB:
    print(x)
0
1
2
100
5
6
7
-5

 

Set의 Method

 

list와 달리 append(맨 뒤에 추가), insert(특정 위치에 추가) 가 아니라, 순서가 없기 때문에 특정 위치를 정할 수가 없다.

그래서 method 이름도 그냥 add / remove이다.

 

Method의 이름들도 가만 보고 있으면 다~ 이유가 있다는 것

앞서, Set에도 add, remove method를 적용하면 값을 추가, 제거할 수 있어서 mutable하다고 했지만,

 

Set안에 있는 각각의 value에 대해선 Imuutable content만 가질 수 있도록 허용한다. 즉 List와 같이 바꿀 수 있는 것을 set의 값으로 입력시킬 수가 없다.

 

이 이유는, set의 가장 강력한 무기인 Hashing을 쓰기 위함인데. hashing은 특정 data를 찾을 때 원 큐에 찾을 수 있도록 해주는 search기법이다. 이 hashing의 조건이 바로 set의 조건처럼 값들이 unordered 되어 있고, 값 자체가 바뀌는 것이 허용되지 않는다는 조건이 필요하다.

 

set의 투박한 특징들을 한 번에 trade - off로 커버할만큼 hashing은 search 속도 면에서 큰 장점을 갖는다.

 

list가 특정 값을 찾기 위해서 list 전체의 data를 싹 순회할 때, hashing은 그 값을 한 번에 딱 찾아낸다.

 

setC = {"C","ABC",3,False}
setC
{3, 'ABC', 'C', False}
1,2,3
setC = {"C","ABC",3,False,[1,2,3]} #set에 list를 넣으려니까 error발생.unhashable type!
setC
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[110], line 1
----> 1 setC = {"C","ABC",3,False,[1,2,3]}
      2 setC

TypeError: unhashable type: 'list'

setC = {"C","ABC",3,False,{1,2,1}}
## 마찬가지로 set 자체로 remove,add 등으로 바뀔 수 있는 container라서 error발생
setC
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[111], line 1
----> 1 setC = {"C","ABC",3,False,{1,2,1}}
      2 setC

TypeError: unhashable type: 'set'

 

6. Tuple (Ordered O, Immutable)

 

List와 같이 순서가 있다. Ordered 구조라 Index로 접근가능하지만, add / remove 등으로 data를 바꾸는 것은 불가능!

 

Tuple을 Declare 할 때는 소괄호를 쓰는데 그냥 (8) 이렇게 하면 그냥 괄호 안에 숫자가 있는 수학적표현이다.

그래서 value를 하나만 쓸 때는 (8,) 이렇게 쉼표를 적어준다.

#튜플은 내부 Data를 바꿀 수 없다!
tup2 = ("A",3,True)
tup2[0] = "B"
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[121], line 2
      1 tup2 = ("A",3,True)
----> 2 tup2[0] = "B"

TypeError: 'tuple' object does not support item assignment

 

※ 그런데, Tuple은 Mutable한 data를 받을 순 있다. 얘네를 바꾸는 건 또 가능 ㅎㅎ 유의하자

tup3 = ("A",[1,2,3],"C")
print(tup3)
tup3[1][0] = "Changed" #tuple 안을 파고들어서 value를 바꾸는 거 가능
print(tup3)
('A', [1, 2, 3], 'C')
('A', ['Changed', 2, 3], 'C')

 

7. Dictionaries ( 처음 배울 땐 별거 아닌 거 같았는데, 나중에 가면 갈수록 은근 많이 쓰인다.. )

 

Ordered O + Mutable without Duplicate 구조이다. Key/Value로 이루어져 있으며 Key는 List의 Index와 같은 역할이라고 보면 된다.

그냥 중괄호를 쓰면 Empty Dictionary를 나타낸다. 이게 우리가 set을 쓸 때 set()으로 Empty Set을 표현했던 이유!

 

#모든 것은 Key 기준으로 이루어진다.
dictA = {"A":1,"B":2}
dictA["C"] = 3
print(dictA)
dictA["A"] = 100
print(dictA)
print("C" in dictA)
{'A': 1, 'B': 2, 'C': 3}
{'A': 100, 'B': 2, 'C': 3}
True

 

for 문도 key 값 기준으로 돌릴 수 있다.

 

for x in dictA:
    print(x,dictA[x])    
A 100
B 2
C 3

 

Dictionary의 각종 method들도 알아두면 유용하다.

'In' Operation도 Key 값 기준으로 적용한다.

 

간단한 예제 case, dictionary의 key - value pair를 swap한 grade_inv를 만들어보자

 

grades = {"A":1,"B":5,"C":3,"D":3}
grades_inv = {}
​
for x in grades:
    if grades[x] not in grades_inv.keys():
        grades_inv[grades[x]] = [x]
    else :
        grades_inv[grades[x]].append(x)
    
    
grades_inv
{1: ['A'], 5: ['B'], 3: ['C', 'D']}

 

Key는 중복되지 않는데, value는 중복된다는 것을 체험해 볼 수 있는 Case였다!

 

 

8. Mutability

 

List, Set, Tuple, Dictionary의 Mutability를 체크해보자. OX 퀴즈에도 많이 나오고 기본적으로 이해하고 있으면 편하다.

 

Mutable의 정의

Mutable 한 Container의 특징은 1) element 들을 추가하거나 삭제할 수 있다. 2) 각 element를 가르키는 화살표를 바꿀 수 있다, 즉 element의 value를 수정할 수 있다는 것이다.

 

List : Mutable / Tuple : Immutalbe(※ element가 mutable하면 그 놈은 수정 가능) 즉 tuple은 Immutable하지만 그것의 element는 mutable 하다. / Set : Mutable 한 container인데(값의 pointing을 바꿀 수 없는데도 일단 mutable하다고 하네) Tuple과 반대로 각 element들은 Immutable(Hashable)하다.

 

그냥 값 변경(추가,삭제,수정)이 가능만 하면 Mutable container라고 표현한다.

set의 value , dict의 key는 모두 immutalbe 해야한다.(int, string, tuple 등) dictA의 value는 mutable해도 상관 X

 

Python 대부분의 일은 for loop , in operator(뭔가를 찾는 것)이 set와 dictionary에서 굉장히 빠르다. Hashing이라는 것이 주는 이점이 그만큼 크다는 것!

 

set value, dict key가 immutable 하다는 조건은 까다롭지만 그것들이 data를 찾을 때 순차적으로 쭉~ 훑는 것이 아닌 Hashing skill을 쓰는 것을 가능케 하니까 빠른 써칭이 가능하게 해준다.

 

Dict의 key 자체가 index! Set의 element 찾기와 같은 원리다. One shot One kill!

 

- E. O. D -