モジュールのインポートと検索パス
Pythonがimport文を実行する際、インタプリタは特定の順序でモジュールファイルを検索します。この検索パスはsys.pathで確認でき、文字列のリスト形式で格納されています。
import sys
print(sys.path)
リストの最初の要素は空文字列('')となることが多く、これは現在の作業ディレクトリを示します。モジュールが見つからない場合は、リスト内のパスを順に探索します。
実行時に動的に検索パスを追加したい場合、リストの操作を行います。insert(0, path)を使用すれば、指定したパスを最優先で探索させることが可能です。
sys.path.insert(0, '/user/local/lib/my_modules')
モジュールの再読み込み
一度インポートされたモジュールは、再度import文を実行しても再読み込みされません。開発中にコードを変更し、その変更を反映させたい場合は、importlib.reload()関数を使用する必要があります。
import my_module
import importlib
# コード変更後
importlib.reload(my_module)
名前空間とスコープ
名前空間とは、変数名や関数名がどの範囲で有効かを管理する概念です。例えば、異なるクラスに同じ名前の生徒がいたとして、放送で「生徒」と呼んでも誰を指すか特定できませんが、「A組の生徒」と呼べば特定できます。この「A組」に相当するのが名前空間です。
変数のスコープ(有効範囲)は、その変数が定義された名前空間に依存します。
# config.py
count = 100
def show_config():
print(f"Config count: {count}")
# main.py
from config import *
count = 999
def show_main():
print(f"Main count: {count}")
show_config() # 出力: Config count: 100 (configの名前空間)
show_main() # 出力: Main count: 999 (mainの名前空間)
関数内からグローバルスコープやローカルスコープの情報を取得するには、組み込み関数globals()とlocals()を使用します。これらはそれぞれの名前空間の辞書を返します。
オブジェクトの比較: is と ==
Pythonには等価性を判定する2つの異なる演算子が存在します。
is: 両者の参照がメモリ上の同一オブジェクトかを判定します(同一性)。==: 両者の値が等しいかを判定します(等価性)。
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # True: 値は同じ
print(a is b) # False: 異なるオブジェクト
__eq__メソッドのカスタマイズ
クラスを定義する際、__eq__特殊メソッドをオーバーライドすることで、==演算子の挙動を独自に定義できます。
class User:
def __init__(self, name):
self.name = name
def __eq__(self, other):
if not isinstance(other, User):
return False
return self.name == other.name
u1 = User("Alice")
u2 = User("Alice")
print(u1 is u2) # False: インスタンスは異なる
print(u1 == u2) # True: __eq__により名前が等しければTrue
オブジェクトのコピー:浅いコピーと深いコピー
浅いコピー (Shallow Copy)
copy.copy()はオブジェクトの浅いコピーを作成します。これは新しいオブジェクトを作りますが、その中身(要素)は元のオブジェクトの参照を共有します。
深いコピー (Deep Copy)
copy.deepcopy()はオブジェクトの深いコピーを作成します。元のオブジェクトと、その中に含まれるすべてのオブジェクトを再帰的に複製するため、完全に独立したコピーになります。
import copy
original = [[1, 2], [3, 4]]
shallow_copied = copy.copy(original)
deep_copied = copy.deepcopy(original)
original[0][0] = 99
print(shallow_copied[0][0]) # 99 (参照を共有しているため影響を受ける)
print(deep_copied[0][0]) # 1 (完全に独立しているため影響を受けない)
不変型における注意点
タプルや文字列などの不変オブジェクトをcopy.copy()した場合、コピーの必要がないため、同一のオブジェクトへの参照が返されます。
import copy
t1 = (1, 2, 3)
t2 = copy.copy(t1)
print(t1 is t2) # True: 同一オブジェクト
また、リストのスライス表記list[:]や、辞書のdict.copy()、list(list)といったコンストラクタ呼び出しも浅いコピーとして機能します。