Nonamed Develog
[TIL][240719] 파이썬 문법 빈틈을 메꾸자 (4) 본문
새로 알게 된 점은 무엇인가?
피보나치 수 구하기
# 1. 코드 작성 전 슈도 코드로 구상
def fibo(n): # 2. 골격 코드 작성
if n == 1: # 4. 종료 조건 작성
return 1 # 5. 종료 조건은 명확하지 않은 경우가 있으므로
elif n == 2: # 재귀 논리를 생각하는 연습하기
return 1
else:
return fibo(n-1) + fibo(n-2) # 3. 구상한 재귀 논리 작성
n = int(input())
print(fibo(n))
멤버십 연산자(in, not in): iterable(반복가능한) 시퀀스에서 True/False 판별
value(확인할 값) in sequence(확인할 시퀀스): 특정 값이 시퀀스에 속해 있을 때 True
value(확인할 값) in sequence(확인할 시퀀스): 특정 값이 시퀀스에 속해 있지 않으면 True
memeber = 'aiden'
memebers = ['aiden', 'bob', 'june', 'haley', 'eric']
print(memeber in memebers) # True
리스트 연결연산자(+)와 반복연산자(*): 가능하면 사용하지 않은 것이 좋다.
list_a = [1, 2, 3]
list_b = [4, 5, 6]
print(list_a + list_b) # [1, 2, 3, 4, 5, 6]
print(list_a * 3) # [1, 2, 3, 1, 2, 3, 1, 2, 3]
set(): 집합(벤 다이어그램)을 의미한다. 항목의 순서와 중복이 없다.
- 따라서 for문을 이용하면 모든 항목을 1회 순회할 수 있지만 순서는 무작위로 나온다.
딕셔너리
- 키(key)과 값(value)이 쌍으로 존재하는 자료형이다. 키를 이용하여 값에 접근이 가능하다.
- dict.key(): 모든 키의 목록 출력
- dict.value(): 모든 값의 목록 출력
- dict.items(): 모든 키와 값 모두 출력
- 반복문 사용 가능
자료구조: 자료형과 메모리
- 컴퓨터는 어떻게 데이터를 기억할까?
- 데이터를 저장할 공간을 메모리에 만들고, 저장할 공간에 대한 주소를 할당받는다.
- 할당받은 주소를 기억했다가 데이터를 해당 주소로 찾아가서 저장한다.
- 마지막으로 데이터가 필요하면 해당 주소로 가서 읽어온다.
- 여기서 문제점이 있을까?
하나의 데이터-하나의 기억-하나의 주소이기 때문에 비효율적이다.
- 여러 기억을 하나의 주소로 찾아가도록 할 수 없을까?
연속적인 공간에 데이터가 저장되도록 조치하면 덩어리 1개 주소만 이용하고 데이터 크기만큼 이동할 수 있다.
- call by value: 불변형인 str int 등을 저장할 때 call my value로 새로운 값을 만든다.
값을 호출하는 것을 의미하며 전달받은 값을 복사하여 처리한다.
전달받은 값을 변경하여도 원본은 변경되지 않는다.
a = 3 # 3을 주소 1706608820528에 저장 후 a라 칭함
b = a # 할당연산자로 같은 주소에 b라 칭함
print(a, "a id:", id(a)) # a 위치의 감&주소
print(b, "b id:", id(b)) # b 위치의 값&주소
a = 10 # 10을 새 주소 1706608820752 저장 후 a를 다시 칭함(b는 현상 유지)
print(a, "a id:", id(a)) # 새로 저장된 주소에 새로 명칭된 a의 값&주소
print(b, "b id:", id(b)) # b는 주소가 변경되지 않음
# 3 a id: 1706608820528
# 3 b id: 1706608820528
# 10 a id: 1706608820752
# 3 b id: 1706608820528
- call my reference: 가변형인 list, dict 등을 저장할 때는 call my reference로 값을 참조하게 주소만 표시한다.
참조에 의한 호출을 의미하며, 전달받은 값을 직접 참조한다.
전달받은 값을 변경할 경우 원본도 같이 변경이 된다.
a = [1, 2, 3, 4, 5] # 리스트를 덩어리로 저장 후 대표 주소 2859937381696 저장 후 a라 칭함
b = a # a라 칭한 주소를 b도 칭함
print(a, "a id:", id(a)) # 2859937381696 대표 주소에 있는 리스트 출력
print(b, "b id:", id(b)) # b도 같은 주소에 있으므로 같은 값 출력
a[0] = 100 # 덩어리의 0번째 인자를 100으로 치환
print(a, "a id:", id(a)) # 변경된 값
print(b, "b id:", id(b)) # 변경된 값
[1, 2, 3, 4, 5] a id: 2859937381696
[1, 2, 3, 4, 5] b id: 2859937381696
[100, 2, 3, 4, 5] a id: 2859937381696
[100, 2, 3, 4, 5] b id: 2859937381696
- 얕은 복사(Shallow copy)
일단 copy와 달리 copy 라이브러리 임포트 후 copy.copy() 메서드로 복사한다.
shallow copy는 원복 객체와 다른 참조값을 생성한다.
a = [0, [1, 2, 3], 'aiden']
b = a[1] # a[1]을 b에 복사
b[1] = 100 #b[1]을 100으로 치환
print(a, id(a)) # 중접된 값이 변경되었으므로 a[1][1]을 100으로 치환
print(b, id(b)) # b[1]을 100으로 치환
# [0, [1, 100, 3], 'aiden'] 2364837724992
# [1, 100, 3] 2364837739840
import copy
a = [1, 2, 3, [4, 5, 6], [7, 8, 9]]
b = copy.copy(a)
print(a, id(a)) # a, b의 주소값이 다르다.
print(b, id(b)) # 객체의 사본을 만든 것
b[1] = 100 # b의 1번째 값을 100으로 치환
print(a, id(a)) # a값 변동 없음
print(b, id(b)) # b의 1번째 값 치환
b[3].append(1000) # b의 3번째 값(리스트)에 1000 추가
b[4][1] = 10000 # b의 4번째 값(리스트)의 1번째 값을 10000으로 치환
print(a, id(a)) # 얕은 복사이기 때문에 중첩된 객체에 원복 객체도 그대로 변경
print(b, id(b)) # 추가값 1000, 치환값 10000
# [1, 2, 3, [4, 5, 6], [7, 8, 9]] 1459450085056
# [1, 2, 3, [4, 5, 6], [7, 8, 9]] 1459450084992
# [1, 2, 3, [4, 5, 6], [7, 8, 9]] 1459450085056
# [1, 100, 3, [4, 5, 6], [7, 8, 9]] 1459450084992
# [1, 2, 3, [4, 5, 6, 1000], [7, 10000, 9]] 1459450085056
# [1, 100, 3, [4, 5, 6, 1000], [7, 10000, 9]] 1459450084992
얕은 복사에 대해 1차 배열(리스트)은 원본 객체를 변경시키지 않은 사본을 생성해 낸다.
2차 이상의 배열, 중첩된 객체는 원본객체를 지키지 못한다. 즉, 완벽한 객체 복사를 할 수 없다.
- 깊은 복사(deep copy)
얕은 복사는 중첩 객체(2차 이상의 리스트)의 영역까지 복사할 수 없었지만 깊은 복사는 중첩 객체도 복사 가능하다.
copy.deepcopy() 메서드 사용
a = [1, 2, 3]
b = a[:] # a[:]를 b에 복사한다. 이 때 deep copy로 새로운 주소와 함께 복사된다.
b[1] = 5 # 복사된 b에 1번째 항목을 5로 치환한다.
print(a, id(a))
print(b, id(b))
# [1, 2, 3] 1726331370816
# [1, 5, 3] 1726331355968
c = [1, 2, 3, 4, 5]
d = c[1:4] # c의 [1:4]를 복사해 새로운 주소에 d로 칭한다.
d[0] = 20 # 새로 칭한 d의 0번째 항목을 20으로 치환한다.
print(c, id(c))
print(d, id(d))
# [1, 2, 3, 4, 5] 1726331356928
# [20, 3, 4] 1726331309952
import copy
a = [1, 2, 3, [4, 5, 6], [7, 8, 9]]
b = copy.deepcopy(a)
print(a, id(a))
print(b, id(b)) # deepcopy로 a의 객체를 가져와 새로운 주소로 복사
b[3].append(1000) # b[3]에 1000 추가
b[4][1] = 10000 # b[4][1]을 10000으로 치환
print(a, id(a)) # a와 b는 중첩되지 않고 다른 객체이기 때문에 영향을 받지 않음
print(b, id(b)) # b[3], b[4][1] 변경
# [1, 2, 3, [4, 5, 6], [7, 8, 9]] 2524982083200
# [1, 2, 3, [4, 5, 6], [7, 8, 9]] 2524982083136
# [1, 2, 3, [4, 5, 6], [7, 8, 9]] 2524982083200
# [1, 2, 3, [4, 5, 6, 1000], [7, 10000, 9]] 2524982083136
a와 b는 깊은 요소를 복사했기 때문에 다른 형태를 갖게 된다.
하지만 deepcopy는 메모리 소요가 심해지기 때문에 만능이라 하더라도 상황을 고려하여 shallow/deep을 결정해야 한다.
무엇을 느꼈고 내일은 무엇을 할까?
알고리즘을 해야 하는데 아직 모르는 게 많다고 느꼈다. 스택과 큐를 직접 구현해 보는 것도 여전히 힘들었다. 강의를 한번 보고 파이썬 월요일 class 관련 세션을 듣고 다시 도전해야겠다.
'WHAT I LEARN > TIL' 카테고리의 다른 글
[TIL][240723] 파이썬 문법 빈틈을 메꾸자 (6) (0) | 2024.07.24 |
---|---|
[TIL][240722] 파이썬 문법 빈틈을 메꾸자 (5) (2) | 2024.07.22 |
[TIL][240718] 파이썬 문법 빈틈을 메꾸자 (3) (0) | 2024.07.18 |
[TIL][240717] 파이썬 문법 빈틈을 메꾸자! (2) (0) | 2024.07.17 |
[TIL][240716] 파이썬 문법 빈틈을 메꾸자! (0) | 2024.07.16 |