Notice
Recent Posts
Recent Comments
Link
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Tags
more
Archives
Today
Total
관리 메뉴

Nonamed Develog

[TIL][240719] 파이썬 문법 빈틈을 메꾸자 (4) 본문

WHAT I LEARN/TIL

[TIL][240719] 파이썬 문법 빈틈을 메꾸자 (4)

노네임드개발자 2024. 7. 20. 19:44

 

새로 알게 된 점은 무엇인가?

피보나치 수 구하기

# 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. 데이터를 저장할 공간을 메모리에 만들고, 저장할 공간에 대한 주소를 할당받는다.
  2. 할당받은 주소를 기억했다가 데이터를 해당 주소로 찾아가서 저장한다.
  3. 마지막으로 데이터가 필요하면 해당 주소로 가서 읽어온다.

- 여기서 문제점이 있을까?

  하나의 데이터-하나의 기억-하나의 주소이기 때문에 비효율적이다.

 

- 여러 기억을 하나의 주소로 찾아가도록 할 수 없을까?

  연속적인 공간에 데이터가 저장되도록 조치하면 덩어리 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 관련 세션을 듣고 다시 도전해야겠다.