포스트

PYTHON › 기본 자료구조(Week1_Day1)

1주차 첫 번째 키워드는 자료구조와 배열이였다.

자료구조

데이터를 구조적으로 표현하는 방식

배열(Array)

하나의 변수가 아니라 묶음 단위로 객체를 저장 각각의 객체는 원소이며 0~ 인덱스를 부여받음.

  • 파이썬에서는 리스트와 튜플로 배열을 구현

사전지식

리스트와 튜플에 대해 알아보기 이전에 mutable과 immutable에 대해 알아야 함.

  • mutable 쉽게 말해 데이터의 변경이 가능한 객체. 대표적으로 list, dictionary, set이 있음.
    1
    2
    3
    4
    
      a = [1, 2, 3, 4]
      print(id(a))
      a.append(5)
      print(id(a))
    

    이때 리스트 a의 id 값을 보면 요소를 추가하기 전 과 후의 값이 같은 것을 알 수 있다.

  • immutable 데이터의 변경이 불가능한 객체. 대표적으로 tuple, 문자열이 있음.

    1
    2
    3
    4
    
     a = (1, 2)
     print(id(a))
     a += (3, 4)
     print(id(a))
    

    이때 튜플이 추가되기 전 후의 id값이 다른 것을 알 수 있다 리스트는 mutable하기 때문에 수정을 하면 기존의 객체가 변경이 되는 것이고, 튜플은 immutable하기 때문에 새로운 객체가 생성되는 것이다. 이때 의문이 들었던 것은 immutable 한 객체 안에 mutable 한 객체를 넣을 수 있다는 것이였다. 분명 불변이라고 했는데 안에 변하는게 들어가면 불변이 아니게 되는게 아닌가??

    1
    2
    3
    4
    
    a = ([1, 2, 3, 4], 2, 'word')
    print(id(a))
    a[0].append(5)
    print(id(a))
    

    결과는 같았다. 찾아보니 ‘불변’의 정의는 튜플 자체의 구조(각요소의 참조, 튜플의 길이 등)이 변하지 않는다는 의미였다. 정리하자면 요소의 위치와 참조는 바꿀 수 없으나 요쇼의 내부상태는 바뀔 수 있다는 말이다.

    리스트

    앞서 언급했는 mutable한 리스트형 객체이다. [] 안에 원소를 쉼표로 구분하며 list()함수를 통해서도 생성 가능하다.

    1
    2
    3
    4
    5
    6
    
    a = list() -> []
    a = list('ABC') -> ['A', 'B', 'C'] or ['A', 'B', 'C', ]로도 표기 가능.
    a = list([1, 2, 3]) -> [1, 2, 3]
    a = list(range(4)) -> [0, 1, 2, 3]
    a = [None] * 5 -> [None, None, None, None, None]
    a = list(123) -> error!!
    

    가 가능하며 리스트의 원소 수는 만들기 전 항상 정해야 한다.

    튜플

    튜플은 원소에 순서를 매겨 결합 한 것으로 immutable 객체이다. 튜플은 () 로 생성하며 리스트와 다르게 생략도 가능하다.

    1
    2
    3
    4
    
    a = ()
    a = 1, -> (1,)
    a = tuple('ABC') -> ('A', 'B', 'C')
    a = tuple(range(4)) -> (1, 2, 3, 4)
    

    튜플과 리스트 모두 언팩을 통해 여러 변수에 할당 할 수 있다.

    1
    2
    3
    4
    5
    
    a, b, c, d = [1, 2, 3, 4]
    print(a) -> 1
    print(b) -> 2
    print(c) -> 3
    print(d) -> 4
    

    배열의 원소에 접근하기

    인덱싱

    • []안에 정수값 인덱스를 지정하는 방식.
    1
    2
    3
    4
    
    a = [1, 2, 3, 4]
    a[0] -> 1
    a[0] = 4 -> [4, 2, 3, 4]
    a[-1] -> 4
    

    이런식으로 접근이 가능하나 이때 주의 할 점은 저렇게 리스트의 원소를 변경했을 때 참조하는 객체의 주소가 바뀔 뿐이라는 것이다. 처음 리스트 a를 선언하게 되면 메모리에 각 원소가 할당되고, 그 원소들을 가리키는 주소를 담는 리스트가 할당되게 된다. 따라서 새로운 원소로 바꾸게 되면 그 원소도 메모리에서 독립적인 공간을 가지게 되고 리스트는 해당 원소의 자리에서 가리키는 주소만 바뀔 뿐이다. 기존의 원소는 더이상 참조되지 않을 경우 가비지 컬렉터의 대상이 되어 메모리에서 할당 해제가 된다.

    슬라이싱

    • 배열의 원소의 일부를 연속해서나 일정한 간격을 두고 꺼내 “새로운” 배열을 만드는 것이다. 대표적인 사용법으로는
    1
    2
    
    a[i:j] -> i번째 인덱스에서 j-1번째 인덱스까지 새로 생성
    a[i:j:k] -> i번째 인덱스에서 j-1번째 인덱스까지 k씩 건너뛰면서 생성.
    

    특이사항은 인덱스와 다르게 len(a)를 벗어나더라도 오류가 발생하지 않는다는 점이다. len(a)보다 크면 len(a)를 지정한 것으로 간주한다. i가 없으면 0, j가 없으면 len(a)로 간주한다. 자주 썼던 건 a[::-1]로 배열의 원소를 반대로 나열할 때 사용했다.

변수 대입 시 주의 사항

  • 변수에 다른 값을 대입하는 것은 기존의 객체는 유지하고 새로운 객체의 식별변호로 변경이 되는 것이다.
1
2
 a = 5
 a = 'abc'

때문에 파이썬에서는 따로 자료형을 선언하지 않고 자동으로 선언해주기 때문에 편리하다. 이때 순서에 유의 해야 한다.

1
2
3
4
5
a = 1
b = 2
a, b = b + 6, a + 3
a -> 8
b -> 4

1
2
3
4
5
6
a = 1
b = 2
a = b + 6
b = a + 3
a -> 8
b -> 7

얕은 복사와 깊은 복사

얕은 복사.copy()

  • 기존에 복사한 리스트의 원소를 변경하면 복사 된 리스트이 원소도 변경 될 수 있다.(테스트 좀 해볼것)

깊은 복사.copy.deepcopy()

  • 기존에 복사란 원소가 바뀌더라도 유지된다.

이런 차이가 발생하는 이유는 얕은 복사의 경우 리스트 안의 모든 원소가 참조하는 곳(최상위 객체) 까지만 복사를 한다. 이게 무슨 의미냐면 리스트의 0번째 인덱스는 a를 가리키고 1번쨰 인덱스는 b를 가리킨다는 것을 알고 있는 객체를 복사한다는 것이다. 때문에 기존의 리스트와 얕은 복사로 생성 된 리스트가 같은 원소를 참조 한다는 것이다. a또는 b가 바뀌게 되면 복사된 리스트도 바뀌게 되는 것이다. 깊은 복사의 경우 재귀적으로 모든 객체를 복사하여 새로 생성한다. 완전히 독립적인 메모리 주소를 가지기 때문에 기존의 리스트의 a가 2를 가리키게 되어도 복사된 리스트의 a는 1을 가리키는 것이다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.