Pythonを使用していると、my_list.pop()を使ってリストの最後の項目を除去したり、my_dict.pop('key')を使って辞書の項目を除去したりするなど、さまざまなオブジェクトで.pop()を使う自分に気づくことがあるでしょう。

まるでpop()がどんなオブジェクトにも付けられる万能インスタンスメソッドのように感じられます。このpop()メソッドの正体は何なのでしょうか?


1. 'pop()'は一つではありません



結論から言うと、listpop()dictpop()名前だけが同じで、実際には全く異なるメソッドです。

  • listクラスはリストに合ったpop()メソッドを独自に定義しています。

  • dictクラスも辞書に適したpop()メソッドを独自に定義します。

  • setクラスも同様です。

このように異なるオブジェクトが同じ名前のメソッドを持つのは、Pythonのオブジェクト指向の特性の一つであるポリモーフィズム(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()がグローバル関数やすべてのオブジェクトの親クラスに定義されているわけではありません。Pythonは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'オブジェクトには'pop'属性がありません。
my_tuple.pop() 

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

要約

  1. pop()はPythonのグローバル関数や共通メソッドではありません。

  2. listdictsetなどそれぞれのコンテナクラスが個別に定義したメソッドです。

  3. 名前が同じ理由は「データを取り出して返す」という慣習的(convention)な意味を共有しているからです。

  4. 動作方法はクラスごとに異なるため、listはインデックスで、dictはキーで、setは任意に項目を除去します。