1. Pythonリスト操作の基本:追加、削除、結合、ソート、そして応用テクニック
  2. Pythonリストの基本:要素の追加と削除
    1. リストの作成と基本的なアクセス
    2. 効率的な要素の追加方法:append()とinsert()
    3. 要素の削除テクニック:remove()、pop()、del
  3. リストの結合とソート:データ整理の必須テクニック
    1. 複数リストの結合方法:+演算子とextend()
    2. リストを整列する:sort()とsorted()の違い
    3. カスタムソートとパフォーマンス考慮事項
  4. Pythonリストの便利関数:len、append、join、map
    1. リストの基本情報を取得:len()関数
    2. 文字列結合の強力な味方:str.join()
    3. コレクションへの関数適用:map()関数
  5. イテレーションを効率化:enumerate、next、Counter
    1. インデックス付きループを実現:enumerate()
    2. イテレータの次要素取得:next()関数
    3. 要素の出現回数を数える:collections.Counter
  6. Pythonリストと「なし」の判定:None、NaN、Queueの活用
    1. 値がないことを示す:NoneTypeの利用と判定
    2. 数値の欠損値表現:float(‘nan’)と判定
    3. リストをキューとして活用:deque()の導入
    4. 参考資料
  7. まとめ
  8. よくある質問
    1. Q: Pythonリストに要素を追加するにはどうすればよいですか?
    2. Q: Pythonリストから要素を削除するにはどうすればよいですか?
    3. Q: Pythonリストを結合するにはどのような方法がありますか?
    4. Q: Pythonリストの要素を並べ替える(ソートする)にはどうすればよいですか?
    5. Q: PythonのQueueの使い方と`get()`メソッドについて教えてください。

Pythonリスト操作の基本:追加、削除、結合、ソート、そして応用テクニック

Pythonのリストは、プログラミングにおいて非常に頻繁に利用されるデータ構造の一つです。順序付けられた変更可能な要素のコレクションであり、数値、文字列、他のリストなど、あらゆる種類のオブジェクトを格納できます。この記事では、Pythonリストの基本的な操作から、データを効率的に扱うための応用テクニックまでを詳しく解説します。

データの追加、削除、整理といった日常的なタスクはもちろん、より複雑なデータ処理においてもリストの理解は不可欠です。本稿では、Python公式ドキュメントを一次情報として、最新かつ正確な情報に基づいたリスト操作の知識を提供します。

Pythonリストの基本:要素の追加と削除

Pythonリストの操作を始めるにあたり、まずリストの作成方法と、要素の追加・削除の基本をマスターすることは不可欠です。これらの操作は、プログラム内でデータを動的に管理するための基盤となります。

リストの作成と基本的なアクセス

Pythonでリストを作成するには、角括弧 [] を使用します。空のリストを作成することも、初期要素を持つリストを作成することも可能です。リスト内の要素には、インデックスを使ってアクセスします。インデックスは0から始まり、リストの最初の要素が0、2番目の要素が1となります。

また、負のインデックスを使用すると、リストの末尾から要素にアクセスできます。例えば、-1 は最後の要素、-2 は最後から2番目の要素を指します。これらの基本を理解することで、リスト内の特定のデータに素早く、そして柔軟にアクセスできるようになります。

# 空のリストを作成
my_list = []

# 初期要素を持つリストを作成
fruits = ["apple", "banana", "cherry"]

# インデックスを指定して要素にアクセス(0から始まる)
first_fruit = fruits[0]  # "apple"
second_fruit = fruits[1] # "banana"

# 負のインデックスで末尾からの要素にアクセス
last_fruit = fruits[-1] # "cherry"
print(f"最初の果物: {first_fruit}, 最後の果物: {last_fruit}")

(出典:Python Documentation: Data Structures – Lists)

効率的な要素の追加方法:append()とinsert()

リストに新しい要素を追加する方法には、主に append()insert() の二つがあります。それぞれのメソッドは異なるシナリオで活用されます。append() メソッドは、リストの末尾に要素を追加する最も基本的な方法です。これは非常に効率的で、ほとんどの場合に推奨されます。

一方、insert() メソッドは、指定したインデックス位置に要素を挿入します。これにより、リストの途中や先頭に要素を追加できますが、挿入位置以降の全ての要素が一つずつ後ろにずれるため、特に大規模なリストの場合にはパフォーマンスに影響を与える可能性があります。どちらのメソッドもリストを直接変更(インプレース操作)するため、追加操作後のリストの状態を把握しておくことが重要です。

fruits = ["apple", "banana", "cherry"]

# append() で末尾に要素を追加
fruits.append("orange")
print(f"append後: {fruits}") # ["apple", "banana", "cherry", "orange"]

# insert() で指定したインデックスに要素を挿入
fruits.insert(1, "grape")
print(f"insert後: {fruits}") # ["apple", "grape", "banana", "cherry", "orange"]

(出典:Python Documentation: Data Structures – Lists)

要素の削除テクニック:remove()、pop()、del

リストから要素を削除する方法も複数存在し、それぞれ異なる使い分けがあります。remove() メソッドは、指定した「値」を持つ最初の要素をリストから削除します。もしその値がリストに存在しない場合、ValueErrorが発生するため、事前に存在を確認するか、例外処理を行うことが推奨されます。

pop() メソッドは、指定した「インデックス」の要素を削除し、その削除された要素を返します。インデックスを指定しない場合、リストの末尾の要素が削除されます。この特性から、pop() はスタックやキューのようなデータ構造をリストで模倣する際によく利用されます。

最後に、del キーワードは、指定したインデックスの要素、またはスライスで指定した範囲の複数の要素を直接削除します。del は特定のインデックスやスライス範囲にある要素を確実に削除したい場合に強力な手段となりますが、削除された要素を返すことはありません。これらの削除方法は、データの整合性を保ちながらリストを管理するために、適切に使い分けることが重要です。

fruits = ["apple", "grape", "banana", "cherry", "orange"]

# remove() で指定した値の要素を削除
fruits.remove("banana")
print(f"remove後: {fruits}") # ["apple", "grape", "cherry", "orange"]

# pop() でインデックスを指定して要素を削除し、その要素を取得
removed_fruit = fruits.pop(1) # "grape" が削除される
print(f"pop(1)後: {fruits}, 削除された果物: {removed_fruit}") # ["apple", "cherry", "orange"]

# del キーワードでスライスを指定して複数要素を削除
numbers = [10, 20, 30, 40, 50]
del numbers[1:3] # インデックス1から3の手前まで(20と30)を削除
print(f"delスライス後: {numbers}") # [10, 40, 50]

(出典:Python Documentation: Data Structures – Lists)

リストの結合とソート:データ整理の必須テクニック

複数のデータセットを統合したり、情報を特定の順序に並べ替えたりすることは、データ処理において非常に重要です。Pythonリストでは、これらのタスクを効率的に行うための強力な機能が提供されています。

複数リストの結合方法:+演算子とextend()

Pythonで複数のリストを結合する方法は、主に二つあります。一つ目は、+ 演算子を使用する方法です。この方法は、二つのリストを結合して「新しいリスト」を作成します。元のリストは変更されないため、既存のデータを保持しつつ新しい結合リストが必要な場合に適しています。しかし、非常に大きなリストを頻繁に結合する場合、新しいリストが生成されるたびにメモリ使用量が増加する可能性があります。

二つ目は、リストの extend() メソッドを使用する方法です。このメソッドは、指定したイテラブル(別のリストなど)の全ての要素を、現在のリストの末尾に追加します。extend() は元のリストを直接変更(インプレース操作)し、新しいリストは作成しません。そのため、元のリストの変更が許容される場合や、メモリ効率を重視する場合に特に有効です。どちらの方法も便利ですが、状況に応じて適切な方法を選択することが重要です。

list1 = [1, 2, 3]
list2 = [4, 5, 6]

# + 演算子で結合 (新しいリストが作成される)
combined_new_list = list1 + list2
print(f"+演算子で結合: {combined_new_list}") # [1, 2, 3, 4, 5, 6]
print(f"元のlist1: {list1}") # [1, 2, 3] (変更なし)

# extend() メソッドで結合 (元のリストが変更される)
list1.extend(list2)
print(f"extend()で結合: {list1}") # [1, 2, 3, 4, 5, 6]

(出典:Python Documentation: Data Structures – Lists)

リストを整列する:sort()とsorted()の違い

リストの要素を並べ替えるソート操作には、sort() メソッドと sorted() 関数という二つの主要な方法があります。これらの違いを理解することは、データの処理において非常に重要です。sort() メソッドはリストの「インプレース」ソートを行います。つまり、リストそのものを直接変更し、要素を並べ替えます。このメソッドは戻り値を返さず(実際には None を返す)、リストを消費してしまうため、元の順序を保持したい場合には使用できません。

一方、sorted() 関数は、任意のイテラブル(リスト、タプル、文字列など)を受け取り、その要素をソートした「新しいリスト」を返します。元のイテラブルは変更されません。したがって、元のデータの順序を保ちつつ、ソートされた結果が必要な場合に最適です。どちらの関数も reverse=True 引数を指定することで降順ソートが可能であり、また key 引数を使ってカスタムソートの基準を定義することもできます。用途に応じて最適なソート方法を選択しましょう。

numbers = [3, 1, 4, 1, 5, 9, 2]

# sort() メソッド (インプレースソート、元のリストが変更される)
numbers.sort()
print(f"sort()後 (昇順): {numbers}") # [1, 1, 2, 3, 4, 5, 9]

numbers.sort(reverse=True) # 降順ソート
print(f"sort()後 (降順): {numbers}") # [9, 5, 4, 3, 2, 1, 1]

original_list = [3, 1, 4, 1, 5]

# sorted() 関数 (新しいソート済みリストを返す、元のリストは変更されない)
sorted_list = sorted(original_list)
print(f"sorted()後: {sorted_list}") # [1, 1, 3, 4, 5]
print(f"元のリスト (変更なし): {original_list}") # [3, 1, 4, 1, 5]

(出典:Python Documentation: Data Structures – Lists, Built-in Functions – sorted())

カスタムソートとパフォーマンス考慮事項

Pythonのソート機能は非常に強力で、単純な数値や文字列だけでなく、複雑なオブジェクトや特定の基準に基づいてソートすることも可能です。これを実現するのが key 引数です。key 引数には、リストの各要素に適用され、その戻り値をソートの比較基準とする関数を指定します。例えば、文字列の長さに応じてソートしたり、オブジェクトの特定の属性に基づいてソートしたりすることができます。これは、データ構造が複雑な場合に非常に役立ちます。

パフォーマンスの観点では、PythonのリストソートアルゴリズムはTimsortと呼ばれるハイブリッドなアルゴリズムを採用しています。これは、実際のデータセットで効率的になるよう設計されており、平均的なケースでは非常に高速です。しかし、非常に大規模なデータセットを扱う場合や、ソートが頻繁に発生するアプリケーションでは、ソートの計算量(一般的にO(N log N))を意識し、不必要なソートを避けるなど、パフォーマンス最適化を検討することも重要です。効率的なkey関数の設計や、既にソートされている部分を活用することで、さらにパフォーマンスを向上させることができます。

words = ["banana", "apple", "cherry", "grape"]

# 文字列の長さに応じてソート
words.sort(key=len)
print(f"長さでソート: {words}") # ['apple', 'grape', 'banana', 'cherry']

# カスタムオブジェクトのソート例
class Item:
    def __init__(self, name, price):
        self.name = name
        self.price = price
    def __repr__(self):
        return f"Item({self.name}, {self.price})"

items = [Item("Book", 2000), Item("Pen", 100), Item("Laptop", 120000)]

# price属性に基づいてソート
items.sort(key=lambda item: item.price)
print(f"価格でソート: {items}")
# [Item(Pen, 100), Item(Book, 2000), Item(Laptop, 120000)]

(出典:Python Documentation: Built-in Functions – sorted(), Python Documentation: Data Structures – Lists)

Pythonリストの便利関数:len、append、join、map

Pythonには、リスト操作をより簡潔かつ強力にするための組み込み関数やメソッドが豊富に用意されています。ここでは、特に頻繁に利用されるlen()append()(再確認)、str.join()、そしてmap()関数に焦点を当て、その使い方と活用法を解説します。

リストの基本情報を取得:len()関数

len() 関数は、Pythonのコレクション型(リスト、タプル、辞書、文字列など)が持つ要素の数を返す組み込み関数です。リストの場合、リストに含まれる要素の総数を整数で返します。この関数は、リストが空であるかどうかの判定(if len(my_list) == 0: または if not my_list:)、ループ処理の回数を決定する際、あるいはデータが特定の条件を満たすかをチェックする際に非常に便利です。

例えば、リストの要素数に基づいて処理を分岐させたり、リストのサイズをユーザーに伝えたりする場合に活用できます。len() はPythonの基本的な操作の一つであり、効率的なコードを書く上で欠かせないツールです。特に、大規模なデータセットを扱う際に、リストの現在のサイズを把握することはメモリ管理やパフォーマンスの最適化にも繋がります。

my_list = [10, 20, 30, 40, 50]
empty_list = []

# len() でリストの要素数を取得
length_of_list = len(my_list)
print(f"my_listの要素数: {length_of_of_list}") # 5

# 空のリスト判定
if len(empty_list) == 0:
    print("empty_listは空です。") # 出力される
if not my_list:
    print("my_listは空です。") # 出力されない

(出典:Python Documentation: Built-in Functions – len())

文字列結合の強力な味方:str.join()

複数の文字列を結合して一つの大きな文字列を作成する際、str.join() メソッドは非常に強力で効率的な選択肢です。このメソッドは、指定した区切り文字列(セパレータ)を、イテラブル(リストやタプルなど)の各要素間に挿入して結合します。例えば、リスト内の単語をスペースで区切って文にしたり、CSV形式のデータ行をカンマで区切って生成したりするのに最適です。

str.join() の大きな利点は、+ 演算子を使った文字列結合と比較して、パフォーマンスが優れている点です。特に多数の文字列を結合する場合、+ 演算子は中間的な文字列オブジェクトを何度も生成するため非効率的ですが、join() は一度に結合処理を行うため、メモリとCPUのオーバーヘッドを削減できます。ただし、join() で結合するリストの要素は全て文字列である必要があります。数値などが含まれる場合は、事前に文字列型に変換する(例: [str(x) for x in numbers] のようにリスト内包表記を使用)必要があります。

words = ["Python", "is", "awesome"]
numbers = [1, 2, 3]

# スペースで結合
sentence = " ".join(words)
print(f"スペース結合: {sentence}") # "Python is awesome"

# カンマとスペースで結合
csv_line = ", ".join(["apple", "banana", "cherry"])
print(f"CSV形式: {csv_line}") # "apple, banana, cherry"

# リストの要素が文字列でない場合 (TypeError)
# invalid_join = "-".join(numbers) # これはエラーになる

# 数値を文字列に変換して結合
string_numbers = [str(x) for x in numbers]
joined_numbers = "-".join(string_numbers)
print(f"数値の文字列変換結合: {joined_numbers}") # "1-2-3"

(出典:Python Documentation: Built-in Types – str.join())

コレクションへの関数適用:map()関数

map() 関数は、リストのようなイテラブルの全ての要素に対して、特定の関数を適用し、その結果をイテレータとして返す組み込み関数です。これは、リスト内の各要素に一律の変換を加えたい場合に非常に役立ちます。例えば、数値のリストの各要素を2乗したり、文字列のリストの各要素を大文字に変換したりするような処理に適しています。

map() の主な利点は、コードが簡潔になり、特に大規模なデータセットに対してメモリ効率が良い点です。なぜなら、map() は結果を一度に全てメモリにロードするのではなく、必要に応じて要素を生成するイテレータを返すためです。もし最終的にリストとして結果が必要な場合は、list() 関数でイテレータをリストに変換できます。リスト内包表記も同様の変換処理を行うことができますが、map() は特に既存の関数を適用する際に、より直感的で簡潔な記述が可能です。lambda 式と組み合わせることで、その場で匿名関数を定義し、柔軟な処理を実現できます。

numbers = [1, 2, 3, 4]

# 各要素を2乗する関数
def square(x):
    return x * x

# map() を使って各要素に square 関数を適用
squared_numbers_iterator = map(square, numbers)
squared_numbers_list = list(squared_numbers_iterator)
print(f"mapで2乗: {squared_numbers_list}") # [1, 4, 9, 16]

# lambda式とmap()の組み合わせ
doubled_numbers = list(map(lambda x: x * 2, numbers))
print(f"lambdaとmapで2倍: {doubled_numbers}") # [2, 4, 6, 8]

(出典:Python Documentation: Built-in Functions – map())

イテレーションを効率化:enumerate、next、Counter

Pythonでリストや他のコレクションを操作する際、単に要素を一つずつ処理するだけでなく、より高度なイテレーション(反復処理)を行うことで、コードをより効率的かつ読みやすくすることができます。ここでは、enumerate()next()、そしてcollections.Counterという三つの強力なツールを紹介します。

インデックス付きループを実現:enumerate()

リストの要素をループ処理する際、しばしば要素そのものだけでなく、その要素がリスト内のどの位置(インデックス)にあるのかも同時に知りたい場合があります。このような時に非常に役立つのが、組み込み関数 enumerate() です。enumerate() は、イテラブル(リストなど)の各要素に対して、インデックスと要素のペアを生成するイテレータを返します。

これにより、従来の for i in range(len(my_list)): のような冗長な書き方を避けて、よりPythonらしい(Pythonicな)コードを書くことができます。また、enumerate() 関数はオプションの start 引数を受け取り、インデックスの開始値を0以外の値に設定することも可能です。これにより、例えば1から始まる番号付けが必要な場合でも、簡単に対応できます。enumerate() は、特にレポート生成や要素の更新、特定のインデックスに基づく条件判定など、多くのシナリオでコードの可読性と効率を向上させます。

fruits = ["apple", "banana", "cherry"]

# enumerate() を使ってインデックスと要素を同時に取得
print("--- 基本的なenumerate ---")
for index, fruit in enumerate(fruits):
    print(f"インデックス {index}: {fruit}")
# 出力:
# インデックス 0: apple
# インデックス 1: banana
# インデックス 2: cherry

# start 引数を指定してインデックスの開始値を変更
print("\n--- start=1 のenumerate ---")
for index, fruit in enumerate(fruits, start=1):
    print(f"項目 {index}: {fruit}")
# 出力:
# 項目 1: apple
# 項目 2: banana
# 項目 3: cherry

(出典:Python Documentation: Built-in Functions – enumerate())

イテレータの次要素取得:next()関数

Pythonでは、リスト以外にもファイルオブジェクト、ジェネレータ、map()filter() の結果など、多くのオブジェクトが「イテレータ」として機能します。イテレータとは、next() 関数を呼び出すたびに次の要素を返すオブジェクトのことです。next() 関数は、イテレータから次の要素を手動で取得するために使用されます。

通常、for ループは内部的に next() を利用していますが、時には特定の条件が満たされるまで要素を一つずつ処理したり、特定の回数だけ要素を取り出したりする必要がある場合があります。このような場合に next() が役立ちます。イテレータにこれ以上要素がない状態で next() を呼び出すと、StopIteration 例外が発生します。これを捕捉することで、イテレータの終了を適切に処理できます。next() 関数には、2番目の引数としてデフォルト値を指定することもでき、StopIteration 例外を回避しつつ、イテレータが枯渇した場合の代替値を返すことができます。

my_list = [10, 20, 30]
my_iterator = iter(my_list) # リストからイテレータを作成

print(f"最初の要素: {next(my_iterator)}") # 10
print(f"次の要素: {next(my_iterator)}") # 20

# デフォルト値を使った例
my_empty_iterator = iter([])
# イテレータが空の場合、デフォルト値として 'No more items' を返す
print(f"空イテレータから取得 (デフォルト値あり): {next(my_empty_iterator, 'No more items')}") # 'No more items'

# イテレータが枯渇した場合の例外処理
try:
    print(f"次の要素: {next(my_iterator)}") # 30
    print(f"さらに次の要素: {next(my_iterator)}") # StopIterationが発生
except StopIteration:
    print("イテレータはもう要素を持っていません。")

(出典:Python Documentation: Built-in Functions – next(), Python Documentation: Glossary – iterator)

要素の出現回数を数える:collections.Counter

リスト内にある各要素の出現回数を効率的に数えたい場合、Python標準ライブラリの collections モジュールに含まれる Counter クラスが非常に便利です。Counter は辞書のサブクラスであり、要素をキーとして、その出現回数を値として格納します。これは、データの頻度分析や、最も頻繁に出現する要素を特定するタスクに最適です。

Counter オブジェクトは、リストや文字列などのイテラブルから直接作成できます。作成後、辞書のように各要素のカウントにアクセスできるだけでなく、most_common(n) メソッドを使って、最も出現回数の多い上位N個の要素とそのカウントを簡単に取得できます。また、Counter オブジェクト同士の加算や減算などの演算もサポートしており、複数のデータセット間の要素の出現頻度を比較する際にも強力なツールとなります。データサイエンスや統計分析の分野で非常に重宝される機能です。

from collections import Counter

data = ["apple", "banana", "apple", "cherry", "banana", "apple"]

# Counter を使って要素の出現回数をカウント
fruit_counts = Counter(data)
print(f"フルーツのカウント: {fruit_counts}")
# Counter({'apple': 3, 'banana': 2, 'cherry': 1})

# 特定の要素のカウントにアクセス
print(f"appleの出現回数: {fruit_counts['apple']}") # 3

# 最も頻繁に出現する要素を取得 (上位2つ)
most_common_fruits = fruit_counts.most_common(2)
print(f"最も多いフルーツ (上位2つ): {most_common_fruits}")
# [('apple', 3), ('banana', 2)]

(出典:Python Documentation: collections — Container datatypes)

Pythonリストと「なし」の判定:None、NaN、Queueの活用

データ処理において、「値がない」状態や「欠損値」を適切に扱うことは、プログラムの堅牢性と正確性を保つ上で不可欠です。Pythonでは、これらを表現するための特定のオブジェクトや、リストの特性を考慮したデータ構造の選択肢が用意されています。ここでは、NoneNaN、そしてキューとしてのリストの活用について見ていきます。

値がないことを示す:NoneTypeの利用と判定

Pythonにおける None は、値が存在しないこと、あるいは何らかの操作が無効であること、または結果がないことを示す特別な定数です。これは NoneType という独自の型を持ち、他の言語のnullやnilに相当しますが、Pythonではオブジェクトとして扱われます。リスト内にプレースホルダーとして None を格納することも可能です。

None の判定には、等値演算子 == ではなく、常にアイデンティティ演算子 is を使用することが推奨されます(my_variable is None)。これは、None がシングルトンオブジェクトであり、常に同じメモリ位置を指すため、is による比較が最も正確で効率的だからです。関数のデフォルト引数として None を使用し、関数内で値が渡されたかどうかを判定するパターンは、Pythonプログラミングで頻繁に見られます。None を適切に利用することで、変数の初期状態、関数の戻り値、あるいはデータベースからの取得結果など、様々な状況での「値の不在」を明確に表現し、ロジックを簡潔に保つことができます。

data_list = [10, None, 20, None, 30]

# None の判定には 'is' を使う
for item in data_list:
    if item is None:
        print(f"値がない要素が見つかりました: {item}")
    else:
        print(f"有効な要素: {item}")

# 関数のデフォルト引数としての None
def process_value(value=None):
    if value is None:
        print("値が提供されていません。デフォルト処理を実行します。")
    else:
        print(f"提供された値: {value} を処理します。")

process_value() # '値が提供されていません。...'
process_value(42) # '提供された値: 42 を処理します。'

(出典:Python Documentation: Built-in Constants – None, Python Documentation: Built-in Types – NoneType)

数値の欠損値表現:float(‘nan’)と判定

数値データを取り扱う際、計算の結果として「無効な数値」や「定義されていない結果」が生じることがあります。Pythonでは、このような欠損値を浮動小数点数型の特殊な値 NaN (Not a Number) で表現します。NaNfloat('nan') または math.nan を使って生成できます。NaN がリスト内に存在する場合、一般的な数値と比較して特別な振る舞いをすることに注意が必要です。

最も重要な特性は、NaN は自分自身を含む他のいかなる値とも等しくないという点です(NaN == NaNFalse になります)。そのため、リスト内に NaN が含まれているかを判定するには、通常の == 演算子ではなく、math.isnan() 関数を使用する必要があります。この関数は、引数が NaN である場合に True を返します。データ分析や科学計算の分野では、欠損値を適切に表現し、処理することはデータの品質を保つ上で非常に重要であり、NaNmath.isnan() はそのための必須ツールとなります。

import math

numerical_data = [1.0, 2.5, float('nan'), 4.0, math.nan, 5.5]

# NaN の判定には math.isnan() を使う
print("--- NaNの判定 ---")
for value in numerical_data:
    if math.isnan(value):
        print(f"NaNが見つかりました: {value}")
    else:
        print(f"有効な数値: {value}")

# NaN との比較の注意点
print(f"NaN == NaN: {float('nan') == float('nan')}") # False
print(f"math.isnan(float('nan')): {math.isnan(float('nan'))}") # True

(出典:Python Documentation: math — Mathematical functions, Python Documentation: Built-in Types – float)

リストをキューとして活用:deque()の導入

リストは、その柔軟性から様々なデータ構造の基本的な構成要素として利用できますが、特定の操作においては効率が低下する場合があります。特に、リストを「キュー」(先入れ先出し、FIFO)として使用しようとすると、問題が生じることがあります。リストの先頭から要素を削除する list.pop(0) や、先頭に要素を挿入する list.insert(0, item) といった操作は、その後の全ての要素を移動させる必要があるため、リストが大きくなるにつれて処理時間が O(n) となり、非常に非効率的になります。

このようなキュー操作を高速に行いたい場合は、標準ライブラリの collections モジュールにある deque (デック; “double-ended queue” の略) クラスを使用することが強く推奨されます。deque は両端からの要素の追加(append(), appendleft())と削除(pop(), popleft())が O(1) の時間複雑度で実行できるため、非常に高速です。これは、幅優先探索(BFS)などのアルゴリズムで探索キューを実装する際や、最近のN個のアイテムを保持するバッファとして利用する際に特に有効です。deque を利用することで、リストで同様の操作を行うよりもはるかに効率的なコードを実現できます。

from collections import deque

# deque を作成
my_queue = deque()

# 要素を末尾に追加 (enQueue)
my_queue.append("task1")
my_queue.append("task2")
print(f"追加後のキュー: {my_queue}") # deque(['task1', 'task2'])

# 要素を先頭から削除 (deQueue)
first_task = my_queue.popleft()
print(f"削除されたタスク: {first_task}") # task1
print(f"削除後のキュー: {my_queue}") # deque(['task2'])

# 先頭に要素を追加
my_queue.appendleft("task0")
print(f"先頭に追加後のキュー: {my_queue}") # deque(['task0', 'task2'])

# リストで同様の操作をする場合の非効率性
# my_list = ["task1", "task2"]
# my_list.pop(0) # O(n) のコストがかかる

(出典:Python Documentation: collections — Container datatypes)

参考資料