개념 사전 · 개념

Python 자료형·참조·복사

파이썬 자료형

자료형은 객체가 지원하는 값과 연산을 정하며 대입은 이름을 그 객체에 연결하는 작업이다. 가변 객체의 내부 변경과 이름 재대입··깊은 복사는 서로 다른 상태 변화를 만든다.

밑줄 용어에 마우스를 올리거나, 키보드로 선택하거나, 터치하면 설명을 볼 수 있습니다.

01무엇이고, 어떤 일을 하나

  • ···을 자료의 순서·중복·키 요구에 맞게 선택한다.
  • 여러 이름과 컨테이너가 공유하는 객체를 추적한다.
  • 입력 자료형과 복사 정책을 명확히 하여 의도하지 않은 공유 변경을 막는다.

02어떤 순서로 동작하나

  1. 객체 생성

    리터럴이나 생성 연산이 객체를 만들고 이름이 그 객체를 참조한다.

  2. 공유 또는 복사

    대입은 참조를 공유하고 는 새 바깥 컨테이너에 기존 내부 참조를 넣는다.

  3. 변경 수행

    append 같은 연산은 대상 객체를 바꾸고 재대입은 해당 이름이나 칸의 참조를 바꾼다.

  4. 관찰 결과 비교

    값 비교와 is를 사용한 동일성, 필요한 내부 깊이의 참조를 함께 보아 어느 변경이 공유됐는지 판단한다.

03먼저 알아둘 용어와 구성요소

는 순서가 있는 가변 시퀀스로 서로 다른 타입의 객체를 참조할 수 있다.
은 요소 참조의 배열을 바꾸지 못하는 시퀀스지만 그 안이 이면 내부 는 변할 수 있다.
는 키와 값을 연결하고 키는 hash 가능한 객체여야 한다.
은 중복 없는 원소 집합이며 인덱스로 순서 접근하는 자료형이 아니다. {}는 빈 이고 빈 은 ()으로 만든다.
copy()나 [:]는 새 바깥 컨테이너를 만들고 기존 요소에 대한 참조를 넣는 얕은 복사다. 따라서 바깥 의 append는 분리되지만 안쪽 를 수정하면 두 바깥 에서 같은 변경이 보일 수 있다.
는 내부 객체 그래프까지 재귀 복사를 시도하고 memo를 사용하여 반복 참조와 순환을 처리한다. 모든 외부 자원·파일·소켓까지 의미 있게 복제해 주는 만능 함수는 아니므로 공유해야 할 객체의 정책을 고려한다.
LEARN THROUGH PRACTICE

True·bool·0b1을 값·자료형·표현으로 나누기

같은 객체를 출력·형 조회·2진수 문자열 변환한 결과를 비교하고 표현 길이와 실제 메모리 크기를 구별한다.

실습 구성과 전제

  • 의 셀 실행 화면을 기준으로 한다. a에 True를 대입한 뒤 세 함수를 호출한다.
  • 실제 수업 화면의 출력과 아래 추가 비교 예제를 구분한다. 2진 문자열의 자릿수로 객체의 저장 크기를 추정하지 않는다.
  1. 하나의 값에 서로 다른 질문을 한다

    • print(a)는 값을 사람이 읽는 형태로 표시하고 type(a)는 객체의 자료형을 반환한다.
    • bin(a)는 정수로 해석 가능한 값을 0b 접두사가 있는 문자열로 표현한다. 객체 자체를 1비트 저장소로 바꾸는 동작이 아니다.
    복습 · 코드와 실행 결과 1원본 크기로 보기 ↗
    • a=True 뒤 출력 순서는 True, <class 'bool'>, 0b1이다.
    • 입력 객체는 동일하지만 type과 bin의 반환 의미가 다르다.
    확인 기준

    세 출력을 값·형·문자열 표현이라는 열로 나누어 적는다.

  2. 수치적 값과 정확한 자료형을 구분한다

    • bool은 int의 하위 형식이어서 True는 정수 연산에서 1에 대응한다. 그렇다고 type(True)가 int로 바뀌지는 않는다.
    • isinstance는 상속 관계까지 허용하는 검사이고 type(x) is T는 정확한 형을 비교한다. 용도에 따라 검사 결과가 다를 수 있다.
    a = True
    print(type(a).__name__)
    print(int(a), bin(a), type(bin(a)).__name__)
    print(isinstance(a, int), type(a) is int)

    추가 비교 예제의 출력은 bool / 1 0b1 str / True False다.

    확인 기준

    0b1은 bin의 반환 문자열이고 int(a)의 값 1과 형식이 다름을 설명한다.

  3. 정수 표현·바이트·문자 인코딩을 나누어 확인한다

    • format(1, '08b')는 8자리 문자열을 만들고 int.to_bytes는 길이가 정해진 바이트열을 만든다. 같은 숫자의 서로 다른 직렬화 방식이다.
    • 문자 '1'을 ASCII 또는 로 인코딩하면 값 0x31인 바이트가 된다. 정수 1을 한 바이트에 넣은 0x01과 같지 않다.
    print(format(1, '08b'))
    print((1).to_bytes(1, 'big').hex())
    print('1'.encode('utf-8').hex())

    각 출력은 00000001, 01, 31이다. 코드의 문자를 메모리 덤프 자체와 혼동하지 않는다.

    확인 기준

    표현을 바꿀 때 입력의 형·출력의 형·길이를 각각 확인한다. 이 출력은 객체의 실제 메모리 점유를 보여 주지 않는다.

실습에서 확인한 동작 원리

  • 자료형은 허용되는 연산을, 인코딩은 외부 바이트 표현을 정한다.
  • 같은 1이라도 bool 값·정수·2진 문자열·문자 바이트는 다른 대상이다.
관련 개념·전체 실습 화면 이어서 읽기 →
동작·명령 참고 문서
LEARN THROUGH PRACTICE

객체 식별자와 실행 중 예외를 실제 출력으로 구분하기

실행 결과가 무엇을 증명하는지 한정하고, 값의 동일성·객체 정체성·예외 발생 지점을 따로 읽는다.

실습 구성과 전제

  • 정수 대입 스크립트와 문자열 검색 셀은 별도 실험이다. 같은 커널의 연속 실행으로 가정하지 않는다.
  • 구현과 실행 방식에 따라 상수 재사용은 달라질 수 있다. 화면의 id 숫자를 다른 컴퓨터에서 재현할 필요는 없다.
  1. 같은 값과 같은 객체를 나누어 읽는다

    • ==는 값 비교이고 is는 객체 정체성 비교다. id는 객체가 살아 있는 동안 유지되는 식별자다.
    • 캡처의 각 스크립트 실행에서는 같은 정수 리터럴이 동일 객체로 재사용된 것으로 읽을 수 있다. 이것을 모든 큰 정수에 보장되는 언어 규칙으로 일반화하지 않는다.
    파이썬 (개인) · 명령·상태 출력 2원본 크기로 보기 ↗
    • 257을 두 이름에 대입한 실행에서 두 id 출력이 같다.
    • 1000으로 바꾼 별도 실행에서도 두 id는 같지만 앞 실행의 숫자와는 다르다.
    확인 기준

    구현·버전·실행 파일 또는 셀 경계를 기록하고, 실제 값 비교에는 ==를 사용한다.

  2. 문법을 통과한 뒤 실패한 호출을 찾는다

    • 문자열 index는 지정 범위에서 처음 일치한 위치를 반환하고 없으면 ValueError를 발생시킨다. end는 포함되지 않는다.
    • 사진에는 앞의 세 호출 출력이 있으므로 해당 셀은 실행을 시작했다. 네 번째 호출의 런타임 예외를 구문 분석 실패와 구별한다.
    python · 코드와 실행 결과 28원본 크기로 보기 ↗
    • 출력 0, 8, 5 다음에 ValueError: substring not found가 나타난다.
    • 화살표는 index('s', 3, 5) 호출을 가리킨다. 위치 5는 이 검색 범위에 포함되지 않는다.
    확인 기준

    문자열 위치를 0부터 세고, start≤위치<end 조건을 직접 대입한다.

  3. 같은 조건을 작은 예제로 재확인한다

    • index의 예외를 예상하고 처리하면 뒤의 검증도 계속할 수 있다. 예외 처리 없이 실행하면 실패한 호출 이후의 문장은 실행되지 않는다.
    • 노트북에서는 셀을 실행한 순서에 따라 변수 상태가 달라진다. 검증할 값의 대입부터 함께 실행해 이전 셀 상태의 영향을 줄인다.
    s = 'sabcdsefs'
    print(s.index('s'), s.index('s', 6), s.index('s', 3, 7))
    try:
        print(s.index('s', 3, 5))
    except ValueError:
        print('검색 범위에 없음')

    관찰한 세 위치와 예외 조건을 한 번에 재구성한 예제다.

    확인 기준

    0 8 5와 범위 없음이 나오는지 확인한다. 객체 id의 특정 숫자는 성공 기준에 넣지 않는다.

실습에서 확인한 동작 원리

  • 실행 출력은 그 실행 조건의 증거이며 구현별 최적화까지 언어의 보편 규칙으로 만들지 않는다.
  • 앞선 출력과 traceback 위치를 함께 보면 파싱·실행·예외 처리 중 어느 단계에서 멈췄는지 설명할 수 있다.
관련 개념·전체 실습 화면 이어서 읽기 →
동작·명령 참고 문서
LEARN THROUGH PRACTICE

얕은 복사의 바깥 목록과 안쪽 목록을 따로 변경하기

객체 복사 뒤 어떤 참조가 분리되고 어떤 참조가 남는지 실제 변경 결과로 추적한다.

실습 구성과 전제

  • 두 사진은 서로 다른 중첩 리스트를 사용한 복사 사례다. 하나의 동일 리스트를 이어 조작한 화면이 아니다.
  • 의 기본 와 copy 모듈을 대상으로 한다. 아래 재구성 코드는 별도로 실행할 수 있는 작은 예시이며 화면의 객체 주소 숫자를 그대로 재현하지 않는다.
  1. 바깥 목록의 항목 교체를 확인한다

    • a.copy()는 새 바깥 리스트를 만들고 원소에 대한 참조를 복사한다. a[0]을 100으로 바꾸는 연산은 a의 첫 번째 칸이 가리키는 객체를 교체한다.
    • b의 첫 번째 칸은 그대로이므로 바깥 리스트가 독립되었다는 사실을 확인할 수 있다. 이것만으로 중첩 객체까지 모두 복사되었다고 결론내리면 안 된다.
    a = [1, 2, [3, 4]]
    b = a.copy()
    print(a is b, a[2] is b[2])  # False True
    a[0] = 100
    print(a, b)  # [100, 2, [3, 4]] [1, 2, [3, 4]]

    동일성 비교와 바깥 항목 교체를 분리한 재구성 코드와 예상 출력이다.

    python · 코드와 실행 결과 16원본 크기로 보기 ↗
    • a=[1,2,[3,4]]에서 b=a.copy()를 만든 뒤 a[0]=100을 수행한다.
    • 그 결과 a의 첫 원소만 100으로 바뀌고 b의 첫 원소는 1로 남아 있다.
    확인 기준

    바깥 리스트의 동일성은 a is b로, 안쪽 리스트의 동일성은 a[2] is b[2]로 각각 비교한다.

  2. 공유된 안쪽 객체의 변경을 추적한다

    • a[2][0]을 바꾸면 먼저 a[2]가 가리키는 내부 리스트를 찾고 그 객체의 첫 칸을 변경한다. b[2]도 같은 내부 리스트를 참조하므로 b로 읽어도 변경이 보인다.
    • 이는 b가 a의 모든 변경을 자동으로 따라가는 기능이 아니다. 두 경로가 같은 가변 객체에 도달하기 때문에 나타나는 결과다.
    a[2][0] = "Hello"
    print(a)  # [100, 2, ['Hello', 4]]
    print(b)  # [1, 2, ['Hello', 4]]

    바로 앞 재구성 코드에 이어 실행했을 때의 예상 출력이다.

    확인 기준

    a[2] 자체를 새 리스트로 교체하는 연산과 a[2][0]을 변경하는 연산을 구분한다.

  3. deepcopy는 내부 객체도 대조해서 검증한다

    • 두 번째 사진에서는 copy.copy와 로 만든 바깥 객체의 id가 다르게 나온다. 바깥 id 세 개만으로 내부 객체가 분리되었다는 사실까지 직접 확인할 수는 없다.
    • 기본 중첩 에서는 로 내부 도 독립 복제된다. 아래처럼 안쪽 동일성과 변경 후 결과를 비교하면 와 깊은 복사의 차이가 드러난다.
    import copy
    x = [["cat"]]
    y = copy.copy(x)
    z = copy.deepcopy(x)
    x[0].append("lion")
    print(y, z)  # [['cat', 'lion']] [['cat']]
    print(x[0] is y[0], x[0] is z[0])  # True False

    별도의 작은 리스트로 재구성한 예시다. 메모리 주소 숫자 대신 동일성과 값 변화를 비교한다.

    python · 코드와 실행 결과 18원본 크기로 보기 ↗
    • copy.copy(a)와 copy.deepcopy(a)를 각각 b와 c에 대입한다.
    • id(a), id(b), id(c)의 출력 값이 모두 다르지만 내부 리스트의 id는 이 사진에서 직접 출력하지 않는다.
    확인 기준

    사용자 정의 객체·외부 자원에는 별도의 복사 의미가 있을 수 있다. 여기의 결과는 기본 예시로 한정한다.

실습에서 확인한 동작 원리

  • 는 바깥 컨테이너를 분리하지만 안쪽 가변 객체의 참조는 공유할 수 있다.
  • 복사는 id 하나보다 참조 경로와 변경 연산을 함께 추적할 때 정확히 설명된다.
관련 개념·전체 실습 화면 이어서 읽기 →
동작·명령 참고 문서

04예제로 따라가기

  • grid=[[]]*3은 독립된 행 세 개가 아니라 안쪽 리스트 하나를 세 칸이 참조하므로 grid[0].append(7)이 모든 칸에 보인다.
  • [[] for _ in range(3)]은 생성식을 세 번 평가하여 별도 내부 리스트를 만든다.
  • d={1:'a'} 뒤 d[True]='b'는 동등한 수 키에 값을 쓰므로 사전 길이가 2가 아닌 1이다.

05혼동하기 쉬운 점과 확인 기준

  • unhashable type 오류는 단순히 문법이 틀렸다는 뜻이 아니다. · 같은 가변 자료를 키로 사용했는지, 내부에 그런 자료가 들어 있는지 확인한다.
  • 두 번째 순회에서 결과가 비면 원본 자료가 사라진 것인지 같은 iterator를 끝까지 소비한 것인지 구분한다. iter()는 새 반복자를 만들지만 이미 소진한 iterator를 로 바꿔도 새 자료가 생기지 않는다.
  • .sort()나 append()는 리스트를 변경하고 None을 반환한다. result = items.sort() 이후 result가 None인 현상을 정렬 실패나 빈 리스트로 처리하지 않는다.

06상세 노트 · 실습 자료

헤더 구조, 설정 예시, 수치 계산과 수업 캡처는 아래 노트에서 이어집니다.

READ · OBSERVE · CONNECT

함께 보는 학습 자료

아래 공식 자료를 본문의 개념·예제와 함께 읽어 보세요. 자료별로 확인할 위치와 관찰할 내용을 적었습니다.

공식 가이드

얕은 복사와 깊은 복사의 객체 관계

모듈 앞부분의 assignment·shallow copy·deep copy 설명에 중첩 리스트 예제를 대입한다. 바깥 컨테이너를 복사했을 때 내부 객체가 공유되는 이유를 확인한다.

읽으면서 확인할 것
  • 대입이 객체 복사를 수행하지 않는다는 뜻
  • 의 새 바깥 객체와 공유된 내부 참조
  • 의 memo가 순환·반복 참조를 처리하는 역할

공식 문서 · 표준

기초 개념과 동작 설명을 대조한 자료입니다. 제품 버전과 실제 설정에 따른 차이는 본문 및 원문에서 확인하세요.