내일배움캠프 14일차

2023년 04월 04일 by barryjung

    내일배움캠프 14일차 목차

[오늘 한일]

  • 알고리즘 문제풀이
  • 장고 강의 수강

 

[오늘 배운점]

문제풀이 진도가 점점 배열 개념이 등장하면서 꽤 어려워졌다.

오늘 접한 배열문제가 있었는데 나중에 한번 정리해보겠다.

(리스트가 아니라 수학적 배열 개념인데 복잡하다.)

 

<자꾸 헷갈리는 리스트에 값 넣기>

리스트에 값을 넣는 방법이 다른 변수들과는 약간 다르다보니,

응용을 하려고 하면 헷갈리는 점이있다.

 

값을 넣는 법은 간단하다. append를 사용하면 된다.

반복문을 이용하면 순차적으로 많은 값을 넣어 줄수도 있다.

그런데, 인덱스를 참조하며 리스트에 값을 넣으려면 어떻게 해야 할까.

 

list = []
for i in range(10):
    list.append(i)
print(list)		#[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

list = []
for i in range(10):
    list[i] = i
print(list)		#IndexError: list assignment index out of range

list = []
for i in range(10):
    list.insert(i,i)
print(list)		#[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

일반적인 append는 index를 참조할 방법이 없다.

그냥 뒤쪽으로 순서대로 입력시킬 뿐이다.

(보통 이렇게만 쓰는게 정상적이긴 하다.)

 

내가 시행착오를 겪은 방법 들은 이렇다.

list[i].append(i) 이렇게 쓰면 될거 같아 해봤지만, 작성법부터 틀린 방법이다.

list[i]가 비었있다면 인덱스 범위 에러가 나오고, list[i]가 있다면 작성법 에러가 나온다.

list[i]만으로 해당 값을 뜻하기 때문에 append를 붙일 수 없는 것이다.

즉, 리스트명[].append는 없는 방법이다.

 

그럼 두번째 블록처럼, list[i] = i를 한다면 어떻게 될까.

이 경우도 저대로 실행한다면 인덱스 범위 에러가 나온다.

만약 리스트를 list = [0,0,0,0,0,0,0,0,0,0]으로 선언해서,

각 인덱스가 존재하는 상태라면 정상적으로 동작하긴 한다.

 

※ 하지만 이렇게 임의의 형태로 리스트를 선언해주는 건 좋지 않다고 생각한다.

   필요한 수만큼 인덱스를 만들어 놓는 방법이 따로 있을 지는 모르겠다.

 

세번째 방법으로 하면 정상적으로 인덱스를 참조하면서 리스트에 값을 넣을수 있다.

정확히 그 인덱스 위치에 값을 넣는다.

만약 인덱스 들에 이미 값이 있다면,

해당 값들을 뒤로 밀어내며 인덱스를 차지시킨다.

 

결론, 문제 풀이를 하며 겪은 상황이지만 결국 이방법으로 풀지는 않았다.

그래도 혹시 써먹을 일이 있을지 모른다.

 


<range문을 이용한 배수 순차 반복>

위에서 부딫혔던 문제이름은 '2차원 배열 만들기'이다.

나는 리스트를 두개 사용해서 결국 문제를 풀었다.

 

입출력 예시와 내가 작성한 코드는 이렇다.

num_list = [1, 2, 3, 4, 5, 6, 7, 8]
n = 2

def solution(num_list, n):
    answer = []
    act_count = 0
    temp_list = []

    for number in num_list:
        act_count += 1
        temp_list.append(number)
        if act_count % n == 0:
            answer.append(temp_list)
            temp_list = []

    return answer
    
print(solution(num_list, n))		#[[1, 2], [3, 4], [5, 6], [7, 8]]

그냥 참고용으로 올려본다.

내가 생각한 동작대로 직관적으로 작성했다.

 

 

사용하는 리스트 갯수를 줄여볼순 없을까 하는 생각에 다른 사람 풀이를 봤는데,

다른 풀이에서 본 방법이 정말 인상깊었다.

def solution(num_list, n):
    answer = []
    for i in range(0, len(num_list), n):
        answer.append(num_list[i:i+n])
    return answer

리스트를 선언을 줄인 것도 멋지지만 range반복문 사용에 대해 크게 배웠다.

 

for i in range(0, 총길이, n)으로 range를 선언하면,

n의 간격으로 i의 숫자 올라가면서 반복된다.

이걸 이용해서 특정 수의 배수의 순차 반복을 하며 각 해당 배수인 i를 얻을 수 있다.

 

마이너스(-)의 사용도 조금 헷갈렸는데 확실히 익혀봤다.

for i in range(큰수, 작은수, -n) 이렇게 작성할 수 있다.

꼭 세번째 칸은 -1일 필요가 없이 -n이면 된다.

대신 음수를 넣으려면 앞의 숫자들은 큰수-작은수 순이여야 한다.

이러면 큰수부터 작은수로 내려가는 n의 배수 순차 반복을 얻을 수 있다.

 

결론, range 반복문에 대해 많은 포인트를 익힐수 있었다.

range 반복문 사용도 능숙해지자.

 


<단순하게 생겨서 어려운 리스트 슬라이스>

그리고 위 풀이에서 리스트 조작 부분만 간단하게 보자면,

list = []
num_list= [1,2,3,4,5,6,7,8]
list.append(num_list[0:2])
print(list)		#[[1,2]]

이렇게 한것이다.

여기서 num_list[0:2]로 리스트에서 부분만 가져온 리스트를 반환했다.

알아보니 이 동작을 슬라이스라고 부른다.

필요한 부분만 잘라서 가져온다는 느낌이다.

즉, 위 풀이에선 이 슬라이스를 이용해서 별도의 임시 리스트 없이 작은 리스트를 만든 것이다.

 

(※결국, 저 슬라이스 한번한번이 리스트의 생성이긴 하다.

임시 리스트가 없으니 작성이 간결한 느낌이다.

생각해보니 보이는 것은 간결해졌으나 동작 상 활용되는 리스트 수는 더 많은 것 같다.)

 

파이썬 문서의 리스트 연산 부분. 링크는 https://docs.python.org/ko/3/library/stdtypes.html#common-sequence-operations이다.

표 중간에 리스트명[:] 연산에 대한 설명이 있다.

기가 막힌 방법.

 

생각해보니, 저번에 리스트 얕은 복사에서도 봤던게 바로 이거였다.

그리고 다른 코드 들에서 꽤 자주 등장하는 걸 볼수 있었다.

 

결론, 눈에는 익었었지만 동작은 오늘 확실히 배울수 있었다.

자주 쓰이는 만큼, 더 사용해보고 확실하게 익히자.

 


<장고에서 중복된 값을 get할 경우>

장고 강의를 따라 실습하다보니,

내 장고 DB에 여러 개의 같은 이름의 ID가 만들어 졌다.

 

그런데 다음 실습에서 그 ID들 때문에 에러가 났다.

장고에서 DB에 object.get을 보냈는데 중복된 값이 있어서 에러가 난 모습이다.

왼쪽은 로그인 동작을 실습할때 난 에러, 오른쪽은 migrate동작 시 난 에러이다.

둘다 원인은 같은 값을 가진 DB데이터이다.

 

관리자 페이지에서 데이터를 지울수 있었다. 오른쪽은 DBeaver 접속 모습.

그래서 중복 데이터를 지우려고 방법을 찾아봤다.

강의에서는 아직 삭제를 안 배운상황.(뒤에서 나오지만, 여기서 막혀서 진도가 멈췄다.)

 

나는 SQL강의를 떠올리며, DBeaver로 연결까지 시켰다.

새 데이터 베이스연결에서 SQLite를 선택해서 파일을 열고,

test connection을 누르니 드라이버 설치 안내가 떠서 설치도 했다.

 

하지만, 거기서 멈추고 나는 관리자 페이지로 접속해서 지우는게 좋겠다고 생각했다.

관리자 페이지도 DB 모델이 바뀌는 바람에 안열리는 상태였는데,

작업한 내용을 어느 정도 돌려놓은 다음 열수 있었다.

그래서 관리자 페이지를 이용해 중복된 user data를 지웠다.

 

 

(※그 상태에서 migrate를 시키니 장고가 기억한 사항을 migrate 시켰다.

    그래서 돌려놓은 작업 내용을 그대로 다시 넣었다.)

제대로 복구를 시켜 migrations에 변경사항이 안 잡히는 모습이다.

(결론, migrate동작은 약간 git의 commit과 비슷하다.

 변경 내용을 추적하고 변경점이 없다면 없다는 것을 알려준다.)

 

 

결론, Model.object.get()은 중복된 데이터에 대해 에러가 날 가능성이 있다.

그래서 주로 primary key값을 이용해서 get()을 하게 된다.