Pythonバックエンド面接対策

Pythonの面接問題と解答 2026

バックエンド開発者向けのPython面接問題を厳選したセットです。各回答は口頭で話すための構成として使えます。まず概念を述べ、具体例を加え、最後にトレードオフに触れてください。

Pythonの模擬面接を開始クレジットカードは不要です。1回分の無料セッションがあります。
英語での技術面接練習非ネイティブ話者が技術面接の合格を目指して練習できるモードです。

Python のコアデータモデル

1Pythonにはどのような組み込みデータ型があり、どれが変更可能(ミュータブル)ですか?

Pythonには、NoneType、bool、int、float、complexなどのスカラー型、str、bytes、bytearrayなどのテキスト・バイナリ型、list、tuple、rangeなどのシーケンス型、そしてdict、set、frozensetなどのコレクション型があります。面接で重要となる区別はミュータビリティ(変更可能性)です。list、dict、set、bytearrayはインプレースで変更可能ですが、数値型、bool、str、bytes、tuple、range、frozenset、Noneは変更不能(イミュータブル)です。実務上留意すべきニュアンスとして、tuple自体の要素への参照は不変ですが、参照先がミュータブルなオブジェクトである場合、その内部状態は変更可能である点が挙げられます。

AI コーチを使ってこの質問に答えてみる

2実際のコードにおいて、list、tuple、set、dict にはどのような違いがありますか?

listは順序を持つ変更可能な(ミュータブルな)シーケンスであり、タスクのキュー、収集した処理結果、変更が発生する順序付きデータに適しています。tupleは順序を持ちますが変更不能(イミュータブル)であるため、固定レコード、関数の戻り値、再代入すべきでない値に適しています。setは一意なハッシュ可能オブジェクトを保持し、要素の存在確認(所属判定)や重複排除に最適です。dictはハッシュ可能なキーと値をマッピングし、インデックス、ルックアップ、JSON形式のようなデータに対する標準的なデータ構造です。最近のPythonではdictも挿入順序を保持しますが、主な目的は依然としてキーに基づくアクセスです。

items = ["a", "b", "a"]
unique = set(items)
index = {"user_id": 42}
point = (10, 20)
AI コーチを使ってこの質問に答えてみる

3isと==の違いは何ですか?

==は通常__eq__を介して、2つのオブジェクトが値として等価であるかを判定します。一方、isは2つの参照がメモリ上のまったく同一のオブジェクトを指しているかどうかを判定します。本番環境のコードにおいて、isは通常、value is Noneのようなシングルトンの検証、同一性が真に重要な場合のvalue is True、または番兵オブジェクトのチェックに限定して使用すべきです。小さな整数のキャッシュや文字列のインターン化のような実装の詳細に依存してはいけません。これらはコード例ではisが機能しているように見えても、セマンティクスとしては誤った選択になります。

value = None

if value is None:
    print("missing")
AI コーチを使ってこの質問に答えてみる

4ハッシュ可能(hashable)なオブジェクトとは何ですか?また、なぜ重要なのでしょうか?

ハッシュ可能なオブジェクトとは、その寿命を通じて変化しないハッシュ値を持ち、そのハッシュ値と整合性のある等価性の比較規則を持つオブジェクトのことです。dictのキーやsetの要素がこの性質に依存しているため、ハッシュ可能性は重要です。変更不能な組み込み型のほとんどはハッシュ可能ですが、変更不能なコンテナ内のすべてのオブジェクトが自動的にハッシュ可能になるわけではありません。例えば、listを含むtupleはハッシュ可能ではありません。面接ではこれと設計を結び付けて説明すると効果的です。オブジェクトをキーとして使用する場合、__eq__や__hash__に関与するフィールドは挿入後に変更してはならず、変更してしまうとdictやset内でそのオブジェクトにアクセスできなくなる恐れがあります。

AI コーチを使ってこの質問に答えてみる

5スライス(負のインデックスやステップを含む)はどのように動作しますか?

スライス `sequence[start:stop:step]` は、`start` を含み、`stop` を除外して、`step` 刻みで要素を取得します。境界値が指定されていない場合は、ステップの方向から推論されます。負のインデックスは末尾から逆算されるため、`s[-1]` は最後の要素を指します。負のステップは右から左へと走査するため、多くのシーケンス型において `s[::-1]` で要素が反転されたシーケンスを生成できます。トレードオフとして、スライスは多くの場合新しいオブジェクトを生成するため、表現力は高いものの、大規模なデータでは常にコストゼロというわけではありません。

text = "interview"

print(text[1:5])
print(text[::-1])
AI コーチを使ってこの質問に答えてみる

関数とスコープ

6Python では関数に引数がどのように渡されますか?

Python では代入によってオブジェクト参照が渡されます。これは一般に「共有渡し(call by sharing)」や「オブジェクト参照の値渡し」と呼ばれます。関数は呼び出し元から渡されたものと同じオブジェクトにバインドされたローカルパラメータ名を受け取ります。オブジェクトが変更可能(ミュータブル)な場合、関数内で変更を加えると呼び出し元でもその変更が反映されます。関数内でパラメータ名に再代入した場合は、ローカルのバインディングのみが変更されます。面接で評価される回答では、その場での変更(インプレースな変更)とローカルな再バインドを明確に区別します。たとえば items.append(...) や items.clear() は呼び出し元のリストを変更しますが、関数内の items = [] はローカルな変数名を新しいリストに向け直すだけです。

def add_item(items):
    items.append("new")

def replace_items(items):
    items = []

values = []
add_item(values)
replace_items(values)

print(values)  # ["new"]
AI コーチを使ってこの質問に答えてみる

7変更可能なデフォルト引数が危険である理由は何ですか。

デフォルト引数の値は、関数が呼び出されるたびではなく、関数が定義されたときに一度だけ評価されます。デフォルト値がリストや辞書(dict)である場合、すべての呼び出しで同一のオブジェクトが共有され、意図せず状態が漏洩する可能性があります。安全なパターンは、`def f(items=None):`とし、関数内部で`items is None`のときに`items = []`を生成することです。小規模なキャッシュなど、共有状態を意図的に利用する稀なケースもありますが、コードを読む人を混乱させるため、明示的に記述してドキュメント化すべきです。

def add_tag(tag, tags=[]):
    tags.append(tag)
    return tags

print(add_tag("python"))
print(add_tag("backend"))  # Same list reused

def safe_add_tag(tag, tags=None):
    tags = [] if tags is None else tags
    tags.append(tag)
    return tags
AI コーチを使ってこの質問に答えてみる

8*argsと**kwargsは何のために使われますか?

`*args`は余分な位置引数をタプルにまとめ、`**kwargs`は余分なキーワード引数を辞書(dict)にまとめます。これらはラッパー、デコレータ、柔軟なAPI、呼び出しの転送(フォワーディング)などに役立ちます。呼び出し側では、`*iterable`が位置引数の値を展開し、`**mapping`がキーワード引数の値を展開します。トレードオフとなるのは明瞭さです。関数に安定した公開インターフェイスがある場合は明示的なパラメータを定義する方が適しており、呼び出しの転送や互換性が重要な境界部分では`args`や`kwargs`を使用する方が適しています。

def call_api(path, *args, **kwargs):
    print(path, args, kwargs)

call_api("/users", 1, 2, timeout=5)

values = [1, 2]
options = {"timeout": 5}
call_api("/users", *values, **options)
AI コーチを使ってこの質問に答えてみる

9PythonはLEGB(Local, Enclosing, Global, Built-in)ルールを用いてどのように変数名を解決しますか?

Pythonは、Local(ローカル)、Enclosing(外側の関数のスコープ)、Global(グローバル/モジュールスコープ)、Builtins(組み込みスコープ)の順で名前を探索します。変数の参照はこの順序に従います。関数内での代入は通常ローカルバインディングを作成するため、ローカル代入の前に名前を参照すると UnboundLocalError が発生します。global は代入の対象をモジュールスコープに向け、nonlocal は外側の関数のスコープに向けます。本番環境の優れたコードでは、状態の流れが不明瞭になるのを避けるため、これらはいずれも控えめに使用すべきです。

total = 0

def add(value):
    global total
    total += value

def counter():
    count = 0

    def increment():
        nonlocal count
        count += 1
        return count

    return increment
AI コーチを使ってこの質問に答えてみる

10クロージャとは何ですか?また、遅延バインディングとは何ですか?

クロージャとは、外側の関数がリターンした後も、その外側のスコープにある変数へのアクセスを保持し続ける関数のことです。これはデコレータやコールバックファクトリ、設定済み関数を作成する際に役立ちます。遅延バインディングとは、自由変数が関数の作成時ではなく、内部関数が呼び出された時点で検索・参照される動作を指します。そのため、ループ内で作成されたラムダ式は、すべてループの最終値を参照してしまうことがあります。これを回避するには、例えば lambda i=i: i のようにデフォルト引数を使って現在の値をキャプチャするか、小さなファクトリ関数を使用します。

callbacks = []

for i in range(3):
    callbacks.append(lambda: i)

print([callback() for callback in callbacks])  # [2, 2, 2]

callbacks = []
for i in range(3):
    callbacks.append(lambda i=i: i)

print([callback() for callback in callbacks])  # [0, 1, 2]
AI コーチを使ってこの質問に答えてみる

OOP、イテレーション、リソース管理

11クラス変数とインスタンス変数の違いは何ですか?

クラス変数はクラスオブジェクト自体に保持され、インスタンスが同名の属性でシャドウイング(覆い隠すこと)しない限り、全インスタンス間で共有されます。インスタンス変数は個々のオブジェクトに保持され、通常は__init__内でself.nameのように代入されます。イミュータブル(不変)なクラス定数であれば通常は問題ありませんが、ミュータブル(可変)なクラス属性をインスタンスごとの状態として誤用するとバグの原因になりがちです。面接では名前解決(参照の探索順序)についても触れると良いでしょう。Pythonはまずインスタンス自身を確認し、その後にクラスやその親クラスを探索します。

class Cart:
    items = []

first = Cart()
second = Cart()

first.items.append("book")
print(second.items)  # ["book"]

class SafeCart:
    def __init__(self):
        self.items = []
AI コーチを使ってこの質問に答えてみる

12Pythonにおける継承、MRO (Method Resolution Order)、および super() はどのように機能しますか?

Pythonはメソッド解決順序(MRO: Method Resolution Order)に従って属性を解決し、これは `ClassName.__mro__` で確認できます。単一継承では単純ですが、多重継承ではPythonはC3線形化アルゴリズムを用いて一貫性のある解決順序を生成します。`super()` は単に「親クラスを呼び出す」という意味ではなく、「MROにおける次の実装を呼び出す」という意味を持ちます。これにより、各メソッドが互換性のある引数を受け取り、各クラスが正しく `super()` を呼び出すことで、協調的多重継承が可能になります。

class A:
    def save(self):
        print("A")

class B(A):
    def save(self):
        print("B")
        super().save()

class C(A):
    def save(self):
        print("C")
        super().save()

class D(B, C):
    pass

D().save()  # B, C, A
AI コーチを使ってこの質問に答えてみる

13イテラブル(iterable)、イテレータ(iterator)、ジェネレータ(generator)の違いは何ですか?

イテラブルとは、リストやファイルオブジェクトのように、`iter()` からイテレータを返せるオブジェクトのことです。イテレータとは状態を保持するオブジェクトであり、`__next__` を実装し、要素を走査し尽くした際には `StopIteration` を送出します。ジェネレータは、`yield` やジェネレータ式を用いてイテレータを簡潔に生成する手段です。ジェネレータは値を遅延評価で生成するためメモリ効率に優れていますが、原則として一度しか走査できず、リストのようにインデックスでアクセスすることはできません。

def read_lines(path):
    with open(path, encoding="utf-8") as file:
        for line in file:
            yield line.strip()
AI コーチを使ってこの質問に答えてみる

14コンテキストマネージャとは何ですか?また、with文はなぜ有用なのですか?

コンテキストマネージャは、__enter__と__exit__によって開始時と終了時の処理を定義するものであり、contextlib.contextmanagerを使って作成することもできます。with文を使用することで、リソース管理を明示的かつ確実に行えます。たとえば、例外が発生した場合でもファイルが確実に閉じられ、ロックが解放され、トランザクションのコミットやロールバックが適切に実行されます。__exit__は例外情報を受け取り、Trueを返すことで例外を抑制することも可能ですが、エラーの抑制は慎重に行う必要があります。

from contextlib import contextmanager

with open("data.txt", encoding="utf-8") as file:
    data = file.read()

@contextmanager
def temporary_setting():
    print("enter")
    try:
        yield
    finally:
        print("exit")
AI コーチを使ってこの質問に答えてみる

15浅いコピー(シャローコピー)と深いコピー(ディープコピー)の違いは何ですか?

浅いコピーは新しい外側のコンテナを作成しますが、ネストされたオブジェクトへの参照は同じものを保持します。深いコピーは可能であればネストされたオブジェクトを再帰的にコピーします。単純でフラットなリストであれば浅いコピーで十分なことが多いです。しかし、ネストされた変更可能な構造体の場合、浅いコピーでは状態が共有されたままになり予期しないバグの原因となることがあります。深いコピーはコストが高く、開かれたファイル、ソケット、データベースセッション、またはオブジェクトの識別性に依存する振る舞いを持つリソースなどに対しては TypeError や PicklingError で失敗する可能性があります。

from copy import copy, deepcopy

original = [[1], [2]]
shallow = copy(original)
deep = deepcopy(original)

original[0].append(99)

print(shallow)  # [[1, 99], [2]]
print(deep)     # [[1], [2]]
AI コーチを使ってこの質問に答えてみる

ランタイム、非同期処理、本番用コード

16GIL (Global Interpreter Lock) とは何ですか。また、バックエンドコードにどのような影響を与えますか?

標準のCPythonにおいて、GIL (Global Interpreter Lock) は一度に1つのスレッドのみがPythonバイトコードを実行できるように制限する仕組みです。そのため、通常のスレッド処理ではCPUバウンド(CPU集約型)のPythonコードを高速化することはできません。一方で、多くのブロッキング操作の待機中にはGILが解放されるため、I/Oバウンドの処理にはスレッドが依然として有効です。CPU負荷の高い処理については、multiprocessing、ネイティブ拡張、ベクトル化されたネイティブライブラリ、または別サービスへの分離を採用します。2026年の面接向けとしては、Python 3.13以降のフリーミューテックス(free-threaded)CPythonビルドについても言及するとよいでしょう。対応コードではGILを無効化できますが、依存関係の検証やパフォーマンス測定が引き続き必要です。

AI コーチを使ってこの質問に答えてみる

17スレッド、プロセス、asyncioはそれぞれどのような場合に選択すべきですか?

ブロッキングI/Oがある場合や、同期APIのみを提供するライブラリを使用する場合はスレッドを選択します。シリアライズやメモリのオーバーヘッドを許容した上で、CPU負荷の高い処理(CPUバウンド)、プロセスの完全な分離、またはクラッシュの影響範囲の限定が必要な場合はプロセスを選択します。多数の同時I/O操作があり、非同期データベースドライバやHTTPクライアントなどの非同期対応ライブラリを利用できる場合はasyncioを選択します。実際の選択は使用するフレームワークや依存関係にも依存し、非同期コードとブロッキング呼び出しを混在させると性能が悪化する可能性があります。

AI コーチを使ってこの質問に答えてみる

18PythonのWebサービスにおいて、async/awaitはどのような用途に適していますか?

asyncとawaitは協調的並行処理(cooperative concurrency)を可能にします。コルーチンがI/O処理をawaitすると、制御をイベントループに返却し、他のタスクを実行できるようにします。これは、データベース、HTTPリクエスト、キュー、ソケットなどの待機が発生するAPIで大きな効果を発揮します。ただし、CPUバウンドな処理が自動的に高速化されるわけではありません。CPU負荷の高い処理は、ワーカー、別プロセス、またはネイティブコードへ切り離さない限りイベントループをブロックします。FastAPIでは、通常の `def` エンドポイントはスレッドプールで実行されますが、`async def` 内で同期的なブロッキング処理を実行するとイベントループをブロックしてしまいます。asyncが役立つのは、呼び出し経路全体で非ブロッキングな非同期I/Oが使用されている場合に限られます。

import asyncio

async def load_user():
    await asyncio.sleep(0.1)
    return {"id": 1}

async def main():
    user, settings = await asyncio.gather(
        load_user(),
        load_user(),
    )
    return user, settings
AI コーチを使ってこの質問に答えてみる

19本番環境のPythonでは、例外をどのように処理すべきですか?

対処可能な最も具体的な例外を捕捉し、リカバリ、コンテキストの追加、またはドメイン固有のエラーへの変換が可能なレイヤーで捕捉します。引数なしの bare `except` や、例外の暗黙的な握りつぶしは避けてください。データ損失や運用上の問題が見逃される原因となります。同じ例外を再送出するには `raise` を使用し、原因となったコンテキストを保持するには `raise NewError(...) from exc` を使用します。優れた本番環境のコードでは、システムの境界でログを記録し、適切なHTTPエラーを返し、機密情報を漏洩させることなく有用な例外メッセージを維持します。

try:
    result = parse_payload(payload)
except ValueError as exc:
    raise InvalidRequest("Invalid payload") from exc
AI コーチを使ってこの質問に答えてみる

20本番環境向けのPythonサービスを構築・テストする場合、どのようなアーキテクチャや設計にしますか?

ドメインロジックをフレームワークやインフラ層のコードから切り離し、サーバーやデータベースがなくてもコア機能の動作を検証できるように設計します。設定値には環境変数や型安全な設定オブジェクトを使用し、print関数の代わりにロギングを用い、境界部分では依存性の注入(DI)を行い、ネットワーク呼び出しには明示的なタイムアウトを設定します。テストに関しては、純粋なロジックに対する単体テスト、データベースや外部境界に対する結合テスト、そして重要な業務フローに対する少数のエンドツーエンド(E2E)テストを組み合わせます。外部システムは明確な境界でモック化し、内部の実装詳細ではなく観測可能な振る舞いを検証します。

AI コーチを使ってこの質問に答えてみる