파이썬을 사용하다 보면 my_list.pop()을 써서 리스트의 마지막 항목을 제거하고, my_dict.pop('key')를 써서 딕셔너리의 항목을 제거하는 등 다양한 객체에서 .pop()을 사용하는 자신을 발견하게 됩니다.

마치 pop()이 어떤 객체에든 붙여 쓸 수 있는 만능 인스턴스 메서드처럼 느껴지기도 합니다. 이 pop() 메서드의 정체는 무엇일까요?


1. 'pop()'은 하나가 아닙니다



결론부터 말하면, listpop()dictpop()이름만 같을 뿐, 실제로는 완전히 다른 메서드입니다.

  • list 클래스는 리스트에 맞는 pop() 메서드를 자체적으로 정의하고 있습니다.

  • dict 클래스 역시 딕셔너리에 맞는 pop() 메서드를 자체적으로 정의합니다.

  • set 클래스도 마찬가지입니다.

이처럼 서로 다른 객체가 같은 이름의 메서드를 가지는 것은 파이썬의 객체 지향 특성 중 하나인 다형성(Polymorphism) 과 관련이 깊습니다. 'pop'이라는 이름은 "데이터 구조에서 항목을 하나 꺼내고(pop) 반환한다"는 관습적인 의미를 공유할 뿐입니다.


2. 클래스별 pop()의 서로 다른 작동 방식

이름이 같다고 해서 작동 방식까지 같은 것은 아닙니다. 각 클래스는 자신의 자료 구조에 맞게 pop()을 구현했습니다.

1. list.pop([index])

  • 동작: 리스트에서 특정 인덱스(index)의 항목을 제거하고 그 항목을 반환합니다.

  • 특징: 인덱스를 지정하지 않으면, 기본값으로 -1이 적용되어 가장 마지막 항목을 제거합니다. 스택(Stack) 자료구조의 'pop' 연산과 동일합니다.

  • 예제:

my_list = ['a', 'b', 'c']

# 인덱스 미지정 (마지막 항목 'c' 제거)
last_item = my_list.pop()
print(f"반환된 값: {last_item}, 남은 리스트: {my_list}")
# 출력: 반환된 값: c, 남은 리스트: ['a', 'b']

# 인덱스 0 지정 ('a' 제거)
first_item = my_list.pop(0)
print(f"반환된 값: {first_item}, 남은 리스트: {my_list}")
# 출력: 반환된 값: a, 남은 리스트: ['b']

2. dict.pop(key[, default])

  • 동작: 딕셔너리에서 특정 키(key)에 해당하는 항목(key-value 쌍)을 제거하고 그 값을 반환합니다.

  • 특징: list.pop()과 달리 인덱스가 아닌 키(key)를 반드시 전달해야 합니다. 만약 키가 없는데 default 값이 주어지면 그 값을 반환하고, default 값도 없으면 KeyError가 발생합니다.

  • 예제:

my_dict = {'apple': 1, 'banana': 2}

# 'apple' 키 제거
item = my_dict.pop('apple')
print(f"반환된 값: {item}, 남은 딕셔너리: {my_dict}")
# 출력: 반환된 값: 1, 남은 딕셔너리: {'banana': 2}

# 없는 키('grape')를 시도 (default 값 99 반환)
default_item = my_dict.pop('grape', 99)
print(f"반환된 값: {default_item}, 남은 딕셔너리: {my_dict}")
# 출력: 반환된 값: 99, 남은 딕셔너리: {'banana': 2}

3. set.pop()

  • 동작: 세트(set)에서 임의의 항목을 제거하고 그 항목을 반환합니다.

  • 특징: 세트는 순서가 없는 자료구조이므로, 어떤 항목이 제거될지 예측할 수 없습니다. pop()에 아무 인자도 전달하지 않습니다.

  • 예제:

my_set = {10, 20, 30}

# 임의의 항목 제거 (어떤 값이 나올지 모름)
item = my_set.pop()
print(f"반환된 값: {item}, 남은 세트: {my_set}")
# 가능한 출력 1: 반환된 값: 10, 남은 세트: {20, 30}
# 가능한 출력 2: 반환된 값: 20, 남은 세트: {10, 30}

3. "어디에나 붙는" 것처럼 보이는 이유



우리가 pop() 을 쓰면서 "인스턴스 메서드" 같다. 라고 생각하는 것은 정확합니다. pop()은 실제로 각 객체(인스턴스)에 속한 메서드입니다.

obj.pop()처럼 보인다고 해서 pop()이 전역 함수이거나 모든 객체의 부모 클래스에 정의된 것은 아닙니다. 파이썬은 obj.method()를 호출할 때, obj라는 객체가 method라는 이름의 속성(메서드)을 가지고 있는지 동적으로 확인합니다.

  • my_listlist 클래스의 인스턴스이고, list 클래스에는 pop이 정의되어 있습니다.

  • my_dictdict 클래스의 인스턴스이고, dict 클래스에도 pop이 정의되어 있습니다.

따라서 my_list.pop()my_dict.pop()이 모두 작동하는 것입니다.

만약 pop() 메서드를 정의하지 않은 객체에서 호출을 시도하면 어떻게 될까요? 예상대로 AttributeError가 발생합니다. 튜플을 예를 들어 보겠습니다.

# 튜플(tuple)은 항목을 변경할 수 없으므로 pop() 메서드가 없습니다.
my_tuple = (1, 2, 3)

# AttributeError: 'tuple' object has no attribute 'pop'
my_tuple.pop() 

An infographic illustrating the pop() method in Python across three data structures

요약

  1. pop()은 파이썬의 전역 함수나 공통 메서드가 아닙니다.

  2. list, dict, set각각의 컨테이너 클래스가 개별적으로 정의한 메서드입니다.

  3. 이름이 같은 이유는 "데이터를 꺼내고 반환한다"는 관습적인(convention) 의미를 공유하기 때문입니다.

  4. 동작 방식은 클래스마다 다르므로, list는 인덱스로, dict는 키로, set은 임의로 항목을 제거합니다.