Skip to content
This page has been auto-translated and may contain errors.View in English

리스트

docs.scrimba.com

변수는 한 가지를 담습니다. 리스트는 많은 것을 순서대로 담으며, 모두 하나의 이름으로 관리합니다. 순위 리스트는 점수가 순서대로 나열된 것입니다. 퀴즈는 문제들의 모음입니다. 관련된 값들의 그룹을 관리해야 할 때, 리스트가 필요합니다.

리스트는 파이썬의 범용 순서형, 가변 시퀀스입니다. 시간에 따라 변하는 모든 것에 자연스럽게 적합합니다: 항목이 추가되거나 제거되고, 순서가 섞이고, 내용이 필터링되거나 정렬됩니다. 순서가 중요하고 컬렉션이 변할 때, 리스트가 보통 첫 번째 선택입니다.

list는 파이썬의 동적 배열입니다: 순서형이고 가변 (제자리에서 변경 가능)인 시퀀스입니다. 성능 특성이 설계 결정을 주도합니다. 인덱스로 읽기는 O(1), 리스트의 크기와 관계없이 상수 시간입니다(big-O는 비용이 크기 n에 따라 어떻게 확장되는지를 나타내는 약칭입니다). append()도 효과적으로 O(1)입니다: 리스트가 끝에 여유 공간을 유지하고 가끔만 크기를 조정합니다. 문제가 되는 비용은 insert()remove()의 O(n)인데, 변경 지점 이후의 모든 요소가 이동하기 때문입니다. 루프에서 반복해서 앞에 삽입하거나 제거하는 자신을 발견하면, O(n)은 다른 자료 구조를 선택해야 한다는 신호입니다.

리스트 생성하기

대괄호, 쉼표로 구분된 값들. 리스트는 어떤 유형이든 섞어서 담을 수 있으며, 빈 리스트도 시간이 지나면서 만들어 나갈 수 있는 시작점으로 유효하고 일반적입니다.

리스트는 괄호 구문으로 정의되며 삽입 순서를 유지합니다. 다른 리스트를 포함한 모든 파이썬 값을 담을 수 있습니다. 빈 리스트 []는 항목을 증분적으로 축적할 때의 표준 시작점입니다.

괄호 리터럴은 새로운 list 객체를 생성하며, 리스트는 요소의 참조를 저장합니다, 복사된 값이 아니라 (참조는 실제 객체가 있는 곳을 가리키는 포인터입니다). 따라서 요소는 다른 리스트를 포함한 모든 파이썬 객체가 될 수 있습니다. 하나의 리스트에 여러 유형을 섞는 것은 유효하지만 빠른 스크립트를 제외하고는 흔하지 않습니다: 유지하는 대부분의 리스트는 한 가지 유형을 담아 추론하기 쉽고 루프에서 처리하기 쉽습니다.

python
scores = [87, 92, 74, 65, 91]
players = ["민준", "지은", "준호"]
mixed = ["민준", 87, True, 3.14]   # any types, though uncommon
empty = []
Juno리스트 생성하기 대괄호 안의 값들을 쉼표로 구분하며, 파이썬은 작성한 순서대로 유지합니다. 빈 []는 채워나갈 계획이 있을 때 좋은 시작점입니다. 제 리스트의 대부분은 그렇게 시작해요, 비어있고 채워질 때를 기다리며.
Juno리스트 생성하기 괄호가 리스트를 정의하고 삽입 순서를 유지합니다. 리스트는 다른 리스트를 포함한 모든 값을 담을 수 있으며, []는 항목을 증분적으로 축적할 때의 표준 시작입니다.
Juno리스트 생성하기 리스트는 요소에 대한 참조를 담아서 모든 객체를 섞을 수 있지만, 실제로 유지하려는 것은 리스트당 한 가지 유형입니다. []로 도달해서 만들어나가세요, 과할당이 그것을 저렴하게 만듭니다.

인덱싱과 슬라이싱

리스트는 문자열과 같은 번호 체계를 사용합니다: 위치는 0부터 시작하고, 음수는 끝에서부터 센다. 위치로 모든 항목을 읽을 수 있습니다. 리스트는 가변이므로, 특정 위치에 쓸 수도 있습니다.

리스트 인덱싱과 슬라이싱은 문자열과 같은 규칙을 따릅니다. 핵심 차이점은 가변성입니다: 인덱스나 슬라이스에 할당해서 제자리에 항목을 변경할 수 있으며, 문자열은 이를 허용하지 않습니다.

인덱싱과 슬라이싱은 문자열과 동일하게 읽히지만, 리스트는 가변이므로 할당할 수 있습니다. 알아야 할 것은 슬라이스 할당입니다: 리스트의 길이를 변경할 수 있습니다. lst[1:3] = [10, 20, 30]은 위치 1과 2의 두 항목을 세 개의 새 항목으로 바꾸며, 리스트가 하나 증가합니다. 바꾸는 것이 슬라이스의 길이와 일치할 필요가 없으며, 이는 편리하고 흔한 놀라움이므로 의도적으로 도달하기보다는 실수로 도달하지 않도록 주의하세요.

python
scores = [87, 92, 74, 65, 91]

scores[0]      # 87  (first)
scores[-1]     # 91  (last)
scores[1:3]    # [92, 74]
scores[:2]     # [87, 92]
scores[::-1]   # [91, 65, 74, 92, 87]  (reversed)

scores[0] = 90   # mutable: works (strings would raise TypeError)
Juno인덱싱과 슬라이싱 위치는 0부터 시작하고, 음수는 끝에서부터 센다, 그래서 scores[-1]은 마지막 것입니다. scores[1:3] 같은 슬라이스는 위치 1과 2를 가진 새로운 리스트를 줍니다. 제가 깨닫는 데 잠깐 걸렸던 부분: 리스트로는 위치에도 쓸 수 있습니다, scores[0] = 90, 문자열은 거부할 것입니다.
Juno인덱싱과 슬라이싱 문자열과 같은 인덱싱과 슬라이싱, 0부터 위치, 음수는 끝에서부터. 차이점은 가변성입니다: 인덱스나 슬라이스에 할당하면 제자리에 리스트를 변경하고, 문자열은 TypeError를 발생시킬 것입니다.
Juno인덱싱과 슬라이싱 문자열 인덱싱처럼 읽히지만 할당할 수 있으며, 슬라이스 할당은 리스트의 크기를 변경할 수 있습니다. lst[1:3] = [10, 20, 30]은 두 항목을 세 항목으로 바꾸며, 의도적으로 도달하고 실수로 도달하는 것은 놀라움입니다.

항목 추가하기

항목을 추가하는 세 가지 방법이 있습니다. append()는 끝에 단일 항목을 추가하며 거의 매번 사용하게 될 것입니다. insert()는 특정 위치에 추가합니다. extend()는 다른 리스트를 병합합니다.

append()는 O(1) 상수 시간입니다(리스트는 여유 공간을 유지하고 가끔만 크기를 조정합니다, 평균 비용은 상수 유지). insert()는 O(n)입니다 왜냐하면 뒤의 요소들을 이동시키기 때문입니다. extend()+=와 동일하며 루프 내에서 반복된 append()보다 더 효율적입니다.

append()는 리스트가 여유 공간을 유지하고 가끔만 크기를 조정하므로 저렴합니다, 평균 비용은 O(1) (길이와 무관한 상수)입니다. insert(0, x)는 함정입니다: 앞에 삽입하는 것은 O(n)입니다 왜냐하면 모든 요소가 한 슬롯 오른쪽으로 이동하기 때문입니다. 루프에서 반복해서 앞에 추가해서 리스트를 만들면, 이것은 전체적으로 O(n 제곱)이 됩니다. 빠른 앞 삽입이 필요하면, 표준 라이브러리에서 collections.deque를 사용하세요, 그 appendleft는 O(1)입니다, 모듈 장에서 다룹니다. extend()는 한 번에 리스트를 늘리므로, 이미 항목이 있을 때 append() 호출 루프보다 낫습니다.

python
scores = [87, 92, 74]

scores.append(65)          # [87, 92, 74, 65]
scores.insert(1, 100)      # [87, 100, 92, 74, 65]
scores.extend([55, 71])    # [87, 100, 92, 74, 65, 55, 71]

흔한 실수: 리스트를 가진 append()는 전체 리스트를 하나의 항목으로 추가하여, 리스트 안의 리스트를 줍니다. 대신 병합하려면 extend()를 사용하세요:

append(x)는 항상 x를 단일 요소로 추가합니다. 리스트를 append()에 전달하면 중첩된 리스트를 얻습니다. 다른 리스트의 모든 항목을 이것으로 병합하려면 extend()를 사용하세요:

append(x)x가 무엇이든 하나의 요소로 추가하므로, 리스트를 건네면 그 리스트를 통째로 중첩합니다. extend(iterable)은 인수를 순회하며 각 요소를 별도로 추가합니다. lst += otherextend와 같은 작업을 하므로, 호출 지점에서 더 명확하게 읽히는 것을 선택하세요.

python
scores.append([55, 71])    # [..., [55, 71]]  nested list, probably wrong
scores.extend([55, 71])    # [..., 55, 71]    merged, correct
Juno항목 추가하기append()는 끝에 하나의 항목을 붙이며 거의 매번 도달하게 될 것입니다. insert()는 위치에 항목을 놓고, extend()는 다른 리스트를 병합합니다. 클래식한 실수: 리스트를 append()하면 리스트 안에 리스트를 얻으므로, 병합하려 할 때는 extend()를 사용하세요.
Juno항목 추가하기append()가 기본값입니다, 끝에 항목 하나씩. insert()는 뒤의 모든 것을 이동시키므로 비용이 더 많습니다. 리스트를 append()하면 중첩되고, extend() (또는 +=)는 항목들을 병합합니다.
Juno항목 추가하기append()는 amortised O(1), insert(0, x)는 O(n), 루프에서 앞에 만들면 조용히 이차가 되며, 거기서는 deque를 선택하세요. 그리고 리스트를 append()하면 통째로 중첩되고, extend() 또는 +=는 병합입니다.

항목 제거하기

항목을 제거하는 네 가지 도구가 있습니다. remove()는 값으로 검색합니다. pop()은 위치로 제거하고 항목을 반환합니다. del은 위치로 제거하며 반환 값이 없습니다. clear()는 전체 리스트를 비웁니다.

remove()는 O(n)입니다: 값으로 첫 번째 발생을 검색하기 위해 스캔합니다. pop()이 인수 없으면 마지막 항목의 경우 O(1)입니다. 다른 위치의 pop(i)는 O(n)입니다 왜냐하면 요소들이 이동하기 때문입니다. del scores[i]pop(i)와 동일하지만 반환 값을 버립니다.

remove(value)는 앞에서부터 스캔하여 각 요소를 비교하다가 일치를 찾으면 뒤의 모든 것을 왼쪽으로 이동합니다: O(n) (비용은 리스트의 길이에 따라 증가합니다). pop()은 끝에서는 O(1)입니다, 아무것도 이동하지 않습니다. 다른 곳의 pop(i)는 같은 이동 이유로 O(n)입니다. 핫 루프에서 임의 위치에서 제거하는 자신을 발견하면, 그 반복된 O(n)은 데이터를 재구조화하거나 다른 컬렉션을 선택해야 한다는 신호입니다.

python
scores = [87, 92, 74, 65, 91]

scores.remove(74)    # removes first occurrence of 74
scores.pop()         # removes and returns last item (91)
scores.pop(0)        # removes and returns item at position 0 (87)
del scores[1]        # removes at position 1, no return value
scores.clear()       # removes everything

리스트에 값이 없으면 remove()ValueError를 발생시킵니다. 확실하지 않으면 먼저 in으로 확인하세요:

python
if 74 in scores:
    scores.remove(74)

remove()는 미스 시 ValueError를 발생시킵니다. in 확인은 추가 O(n) 스캔을 추가하므로, 리스트를 두 번 순회합니다. 일회성 코드의 경우 괜찮습니다. 실패를 사전 확인보다 잡으려 할 때, try / except ValueError파일과 예외 장에서 다룹니다.

in 그 다음 remove() 패턴은 두 O(n) 스캔, 리스트가 두 번 순회됩니다. 순서가 중요하지 않으면, 대상을 마지막 요소와 바꾸고 팝 하는 것은 O(1) (상수 비용, 이동 없음)입니다. 그리고 멤버십을 많이 테스트한다면, set은 O(1)에서 조회하며 리스트는 O(n)이고, 튜플과 집합 장에서 다룹니다. 경험칙: 순서가 정해진 변하는 데이터는 리스트, "거기 있나"가 핫 질문인 순간은 set입니다.

Juno항목 제거하기remove()는 값으로 삭제하며 첫 번째 일치만, 값이 없으면 ValueError를 발생시키므로, 확실하지 않을 때는 먼저 in으로 확인하세요. pop()은 위치로 제거하고 항목을 반환하며, del은 위치로 제거하고 아무것도 반환하지 않습니다.
Juno항목 제거하기remove()는 첫 번째 값 일치를 삭제하며 미스 시 ValueError를 발생시킵니다. 끝에서의 pop()은 저렴하고 항목을 반환합니다; 다른 곳의 pop(i)는 뒤의 모든 것을 이동시킵니다. del은 위치로 제거, 반환 없음.
Juno항목 제거하기 `remove()`와 끝이 아닌 다른 곳의 `pop(i)`는 O(n) 이동; 꼬리에서의 `pop()`만 O(1). "거기 있나"가 핫 경로면, `set`은 리스트가 O(n)인 O(1)에서 합니다. `remove()` 전에 `in`으로 사전 확인하면 리스트를 두 번 순회합니다.

정렬

sorted()는 새로운 정렬된 리스트를 반환하고 원본을 건드리지 않습니다. .sort()는 제자리에 리스트를 정렬하고 None을 반환합니다. 이 차이가 들리는 것보다 더 중요합니다.

sorted()는 안전한 기본값입니다: 원본을 절대 수정하지 않습니다. .sort()는 제자리에 수정하고 None을 반환하며, 이는 흔한 함정입니다. .sort()의 결과를 할당하면 None을 얻습니다, 정렬된 리스트가 아닙니다. 원본이 그대로 필요할 때는 sorted()를 사용하세요; 정렬된 버전만 원할 때는 .sort()를 사용하세요.

파이썬의 정렬은 안정합니다: 같은 요소들은 원래 상대 순서를 유지합니다. 이는 한 키로 정렬한 다음 다른 키로 다시 정렬해도 첫 번째 순서가 타이브레이커로 살아남도록 합니다. 또한 거의 정렬된 데이터에서 빠르며, O(n) (선형)에 가깝고 처음부터의 O(n log n) 정렬보다 나으므로, 거의 정렬된 리스트를 다시 정렬하는 것은 저렴합니다. .sort()가 의도적으로 None을 반환합니다: 제자리에 리스트를 변경하는 메서드는 또한 그것을 반환하지 않습니다, 이는 x = lst.sort()를 작성하고 조용히 None을 얻는 것을 막는 관례입니다. sorted()는 리스트뿐 아니라 모든 이터러블을 취하며 항상 새로운 리스트를 반환합니다.

python
scores = [87, 42, 96, 55, 71]

ranked = sorted(scores)            # [42, 55, 71, 87, 96] (new list)
scores.sort()                      # sorts in place, returns None
scores.sort(reverse=True)          # [96, 87, 71, 55, 42]

result = scores.sort()             # result is None, not the sorted list
Juno정렬sorted()는 새로운 정렬된 리스트를 주며 원본은 그대로 둡니다. .sort()는 리스트를 재배열하고 `None`을 반환하므로, x = scores.sort()는 리스트가 아닌 None을 줍니다. 저는 그 정확한 실수를 한 번 이상 했어요.
Juno정렬sorted()는 안전한 기본값입니다, 원본을 절대 건드리지 않습니다. .sort()는 제자리에 정렬하고 `None`을 반환하므로, 그 결과를 할당하는 것이 기억할 함정입니다. 원본이 필요할 때는 sorted()를 선택하세요.
Juno정렬 정렬은 안정적이므로, 보조 키로 먼저 정렬하고 주 키로 하면, 이전 순서가 동점을 유지합니다. 거의 정렬된 데이터에서 거의 선형으로 실행되므로 다시 정렬하는 것은 저렴합니다. .sort()는 설계상 `None`을 반환합니다; sorted()는 모든 이터러블을 취하고 새로운 리스트를 반환합니다.

유용한 연산

파이썬은 리스트에 직접 작동하는 내장 도구 세트를 가지고 있습니다. len(), sum(), min(), max()는 항상 도달할 네 가지입니다.

내장 시퀀스 함수는 모든 리스트에서 작동합니다. in은 리스트에서 선형 스캔입니다; 빠른 반복 멤버십 테스트가 필요하면, 세트로 변환하세요. .index()는 값이 발견되지 않으면 ValueError를 발생시킵니다.

len(), sum(), min(), max()는 리스트뿐 아니라 모든 이터러블에서 작동하므로, 같은 호출이 리스트, 튜플, 또는 제너레이터를 읽습니다. 중요한 비용 참고: in.index() 둘 다 앞에서 뒤로 스캔하며, O(n) (비용은 길이에 따라 증가)합니다, 왜냐하면 리스트는 내용의 인덱스가 없기 때문입니다. 루프에서 반복된 멤버십 확인이 이것이 더해지는 곳이며, set은 해결책입니다. 한 가지 날카로운 모서리: sum()0부터 시작하므로 숫자를 더하며, 문자열이 아닙니다; 문자열을 합치려면 "".join()을 사용하세요, 문자열 장에서 다룹니다.

python
scores = [87, 92, 74, 65, 91]

len(scores)          # 5
sum(scores)          # 409
min(scores)          # 65
max(scores)          # 92
scores.count(87)     # 1
scores.index(74)     # 2
74 in scores         # True
74 not in scores     # False
scores.copy()        # shallow copy
scores.reverse()     # reverses in place
Juno유용한 연산len(), sum(), min(), max()는 모두 리스트에 직접 작동하며, 설정이 필요 없습니다. in은 뭔가 있는지 묻고, .count()는 몇 번 나타나는지 세며, .index()는 첫 번째 일치의 위치를 찾습니다.
Juno유용한 연산 내장 함수는 모든 리스트에서 작동하며, 시퀀스 함수는 모든 이터러블에서 작동합니다. in은 선형 스캔이므로, 반복된 멤버십 테스트는 세트로 변환하세요. .index()는 값이 발견되지 않으면 `ValueError`를 발생시킵니다.
Juno유용한 연산len(), sum(), min(), max()는 리스트뿐 아니라 모든 이터러블을 취합니다. in.index()는 둘 다 O(n) 스캔이므로, 핫 멤버십 확인은 set을 원합니다. 그리고 sum()은 `0`부터 시작하므로, 문자열의 경우 `"".join()`을 도달하세요.

반복 처리

for 루프는 리스트를 한 번에 하나의 항목으로 순회합니다. for 뒤의 변수는 순서대로 각 항목을 받습니다. 위치도 필요할 때, enumerate()는 수동 카운터 없이 둘 다를 줍니다.

for item in list는 리스트의 이터레이터를 호출하고 각 단계에서 진행합니다. enumerate(iterable, start=0)는 이터레이터를 래핑하고 (index, value) 쌍을 생성합니다. enumerate()를 사용하는 것은 카운터 변수를 유지하는 것보다 더 깔끔하고 오류가 적습니다.

enumerate()는 루프할 때 (index, value) 쌍을 생성하며, 이는 왜 for i, item in enumerate(...)가 깔끔하게 읽히는지입니다: 각 쌍은 두 이름으로 언팩됩니다. start 인수는 루프가 실제로 리스트를 어디서 읽는지를 변경하지 않고 디스플레이만 이동시킵니다 (그래서 start=1은 하나부터 번호를 매깁니다). 손으로 굴린 카운터보다 선호하는 이유는 속도가 아니라, 손으로 꼬까 증가시키는 별도의 카운터가 증가시키는 것을 깜빡하기 쉬운 한 가지 더 많은 것이며, enumerate()는 동기화를 벗어날 수 없습니다.

python
players = ["민준", "지은", "준호"]

for player in players:
    print(player)

for i, player in enumerate(players, start=1):
    print(f"{i}. {player}")
# 1. 민준
# 2. 지은
# 3. 준호

forenumerate()제어 흐름 장에서 전체 처리됩니다. 짧은 버전: for player in players는 항목당 한 번 실행되고, enumerate()는 모든 반복에서 위치와 값을 줍니다.

Juno반복 처리 `for` 루프는 리스트를 한 번에 하나의 항목씩 순회하며, `for` 뒤의 이름은 매 차례 그 항목이 됩니다. 위치도 원할 때, `enumerate()`는 둘 다를 주므로, 카운터를 직접 유지할 필요가 없습니다. 저를 위해 세게 놔두는 것이 많은 off-by-one 실수를 저장했습니다.
Juno반복 처리for item in list는 순서대로 각 항목을 방문합니다. enumerate(iterable, start=0)는 `(index, value)` 쌍을 생성하며, 손으로 관리하는 카운터보다 더 깔끔하고 오류가 적습니다. `start=1`을 원할 때 1부터 번호 매기는 것을 사용하세요.
Juno반복 처리enumerate()는 `(index, value)` 쌍을 생성하여 두 이름으로 곧장 언팩되고, `start`는 표시된 카운터만 이동시키며 읽기 위치가 아닙니다. 승리는 속도가 아니라, 수동 카운터는 동기화를 벗어날 수 있고 이것은 할 수 없다는 것입니다.

중첩된 리스트

리스트는 다른 리스트를 포함할 수 있습니다. 이것이 그리드나 테이블을 표현하는 방법입니다: 행들의 리스트, 각 행이 값들의 리스트입니다. 두 세트의 대괄호는 항목에 접근합니다: 첫 번째는 행을 선택하고, 두 번째는 열을 선택합니다.

중첩된 리스트는 다른 리스트 참조들의 리스트입니다. 각 내부 리스트는 독립적인 객체입니다. 연결된 서브스크립트로 접근합니다: grid[row][col]. 내부 리스트를 변경하는 것은 외부 리스트에 영향을 미칩니다 왜냐하면 외부 리스트가 같은 객체에 대한 참조를 유지하기 때문입니다.

중첩된 리스트는 실제 2D 배열이 아닙니다. 외부 리스트는 내부 리스트들에 대한 참조를 유지하며, 각각 독립적인 객체이므로, 행들이 다른 길이를 가질 수 있고 다른 유형을 유지할 수 있습니다. grid[1][2]는 두 번의 조회입니다: 행을 선택하고, 그 안에 인덱싱합니다. 후유증이 잡는 것: 외부 리스트의 얕은 복사 (.copy()가 만드는 종류)는 외부 컨테이너를 중복하지만 같은 내부 리스트들을 가리키고 있으므로, 내부 행에 대한 변경은 두 복사본 모두에서 표시됩니다. 아래의 가변성 섹션이 이것을 구체적으로 만듭니다.

python
grid = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
]

grid[0]       # [1, 2, 3]
grid[1][2]    # 6  (row 1, column 2)
Juno중첩된 리스트 리스트는 다른 리스트를 담을 수 있으며, 이것이 그리드나 테이블을 만드는 방법입니다: 행들의 리스트, 각 행이 리스트입니다. 두 세트의 괄호가 안쪽에 도달하며, grid[1][2], 첫 번째는 행을 선택하고, 두 번째는 선택합니다.
Juno중첩된 리스트 중첩된 리스트는 다른 리스트들에 대한 참조의 리스트이며, 각 내부 리스트는 고유의 객체입니다. 연결된 서브스크립트로 도달하며, grid[row][col]. 외부 리스트만 참조를 유지하므로, 내부 리스트를 변경하는 것이 그것을 가리키는 모든 이름을 통해 표시됩니다.
Juno중첩된 리스트 진정한 2D 배열이 아닙니다: 외부 리스트는 참조를 유지하므로, 내부 행은 길이를 바꾸고 유형이 다를 수 있으며, `grid[1][2]`는 두 번의 조회입니다. 얕은 복사는 외부 리스트를 중복하지만 내부 것들을 공유하며, 이것이 다음 섹션이 걸리는 함정입니다.

가변성: 함정

이것은 거의 모두를 놀라게 합니다. 리스트를 새로운 변수에 할당하는 것은 복사본을 만들지 않습니다. 두 이름은 같은 리스트를 가리킵니다. 하나를 변경하면 다른 하나도 변경합니다. 독립적인 복사본을 얻으려면 명시적으로 요청해야 합니다.

리스트 할당은 참조를 복사하며, 객체가 아닙니다. 두 이름은 같은 기초 리스트를 가리킵니다. 어느 이름으로든 변경은 같은 데이터에 영향을 미칩니다. 독립적인 데이터가 필요할 때, .copy(), list(), 또는 전체 슬라이스 [:]로 명시적으로 복사하세요.

b = a는 같은 리스트 객체에 두 번째 이름을 가리키므로, b를 통한 모든 변경은 a가 보는 객체에도 도달합니다. .copy()a[:]얕은 복사를 만듭니다: 원본이 한 새로운 외부 리스트는 같은 요소 참조를 유지합니다. 불변 값 (숫자, 문자열, 제자리에서 변경할 수 없는 것)의 평면 리스트에 대해 그것이 필요한 모든 것입니다. 중첩된 리스트의 경우 함정입니다, 왜냐하면 내부 리스트는 여전히 원본과 복사본 사이에서 공유되므로, 한 쪽을 통한 행 편집이 다른 쪽에도 표시됩니다.

python
a = [1, 2, 3]
b = a            # b is not a copy; it points at the same list

b.append(4)
print(a)         # [1, 2, 3, 4]  (changed: a and b are the same list)
python
b = a.copy()    # independent copy
b = list(a)     # same result
b = a[:]        # also the same

# Nested lists still share their inner objects:
matrix = [[1, 2], [3, 4]]
copy = matrix.copy()

copy[0].append(99)
print(matrix)   # [[1, 2, 99], [3, 4]]  (inner list was shared)

내부 리스트가 각각 완전히 독립적인 중첩된 구조의 경우, 각 내부 리스트를 수동으로 복사하거나, 표준 라이브러리에서 copy.deepcopy()를 사용하세요, 모듈 장에서 다룹니다.

Juno가변성: 함정b = a는 복사본을 만들지 않습니다, 두 이름은 같은 리스트를 가리키므로, 하나를 통한 변경이 다른 하나에 표시됩니다. 독립적인 리스트의 경우 물어봐야 합니다: .copy(), list(a), 또는 a[:]. 이것은 처음에는 거의 모두를 놀라게 합니다, 저도 포함.
Juno가변성: 함정 할당은 참조를 복사하며, 리스트가 아니므로, b = a는 한 객체에 두 이름을 남기고 어느 것으로든 변경이 같은 데이터를 칩니다. 독립성이 필요할 때 .copy(), list(a), 또는 a[:]로 명시적으로 복사하세요.
Juno가변성: 함정 `b = a`는 한 객체를 공유합니다; .copy()a[:]는 얕은 복사를 만들며, 같은 내부 참조들 위의 새로운 외부 리스트입니다. 불변 값의 평면 리스트는 그 방식으로 안전하며, 중첩된 것들은 행을 공유하므로, 내부도 독립적일 필요할 때 copy.deepcopy()를 도달하세요.

더 많은 메서드

메서드하는 일
.append(item)끝에 추가
.insert(i, item)위치 i에 삽입
.extend(iterable)이터러블의 모든 항목 추가
.remove(value)값의 첫 번째 발생 제거
.pop(i)위치 i의 항목 제거 및 반환 (기본: 마지막)
.clear()모든 항목 제거
.index(value)첫 번째 발생의 위치
.count(value)발생 수
.sort()제자리에 정렬
.reverse()제자리에 역순
.copy()얕은 복사 반환

실제로

점수 추적 만들기: 결과를 추가하고, 정렬하고, 요약을 출력하세요.

python
scores = []

scores.append(87)
scores.append(54)
scores.append(92)
scores.append(67)
scores.append(45)

scores.sort(reverse=True)

print(f"Ranked scores: {scores}")
print(f"Highest: {scores[0]}")
print(f"Lowest:  {scores[-1]}")
print(f"Average: {sum(scores) / len(scores):.1f}")
print(f"Top 3:   {scores[:3]}")

이름과 점수의 두 평행 리스트: 최고 성능자를 찾고 순위 결과를 인쇄하세요.

python
names = ["민준", "지은", "준호", "나리"]
scores = [87, 74, 92, 55]

best_score = max(scores)
best_index = scores.index(best_score)
best_player = names[best_index]

print(f"Top player: {best_player} ({best_score})")
print(f"Average:    {sum(scores) / len(scores):.1f}")

ranked = sorted(scores, reverse=True)
print(f"Distribution (ranked): {ranked}")

for rank, score in enumerate(ranked, start=1):
    print(f"  Rank {rank}: {score}")

별칭과 복사 간의 차이, 그리고 중첩된 리스트의 얕은 복사와 깊은 복사 간의 차이를 보여줍니다.

python
# Aliasing: b is not a copy
a = [1, 2, 3]
b = a
b.append(4)
print(a)    # [1, 2, 3, 4]  (same object)

# Shallow copy: outer list is independent, inner lists are shared
matrix = [[1, 2, 3], [4, 5, 6]]
shallow = matrix.copy()
shallow[0].append(99)
print(matrix)    # [[1, 2, 3, 99], [4, 5, 6]]  (inner list shared)

# Manual deep copy with a for loop (no imports needed)
matrix = [[1, 2, 3], [4, 5, 6]]
deep_copy = []
for row in matrix:
    deep_copy.append(row[:])    # copy each inner list explicitly

deep_copy[0].append(99)
print(matrix)    # [[1, 2, 3], [4, 5, 6]]  (unchanged)