Python(パイソン)は、シンプルで強力なプログラミング言語として広く知られていますが、その背後には多くの隠れた特徴や奇妙な動作が存在します。これらの「隠れた機能」を理解することは、Pythonを深く理解し、効率的に活用するために重要です。今回は、Pythonの「隠れた奇妙な特徴」について、完全かつ包括的に紹介します。
1. is
と ==
の違い
Pythonでは、==
と is
は似ているようで異なります。==
はオブジェクトの値を比較しますが、is
はオブジェクトのID(メモリアドレス)を比較します。この違いが原因で、期待しない動作が発生することがあります。

例えば、次のようなコードを考えてみましょう:
pythona = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # True
print(a is b) # False
上記の例では、a
と b
は異なるオブジェクトであるため、is
の比較結果は False
ですが、==
の比較では同じ値を持つリストなので True
となります。
2. ミュータブルとイミュータブルの違い
Pythonでは、オブジェクトは「ミュータブル(変更可能)」と「イミュータブル(変更不可)」に分けられます。ミュータブルオブジェクト(リストや辞書など)はその内容を変更できますが、イミュータブルオブジェクト(タプルや文字列など)は内容を変更できません。この違いが原因で、予期せぬバグが発生することがあります。
例えば、リストはミュータブルであり、その内容を変更できるため、参照を共有している場合に予期せぬ変更が生じることがあります。
pythona = [1, 2, 3]
b = a
b.append(4)
print(a) # [1, 2, 3, 4]
一方、イミュータブルなタプルの場合は、変更を加えようとするとエラーが発生します。
pythona = (1, 2, 3)
# a[0] = 4 # TypeError: 'tuple' object does not support item assignment
3. Pythonのelse
ブロックの謎
Pythonでは、if
、for
、while
などのブロックにelse
を使用することができますが、これが少し変わった動作をすることがあります。特に、for
やwhile
ループの後にelse
を使うと、ループが正常に終了したときにのみelse
が実行されます。ループがbreak
で中断された場合、else
は実行されません。
pythonfor i in range(5):
if i == 3:
break
else:
print("ループが正常に終了しました")
このコードでは、break
でループが中断されるため、else
ブロックは実行されません。
4. __new__
と __init__
の違い
Pythonのクラスには__init__
メソッドが一般的に使用されますが、実は__new__
というメソッドも存在します。__new__
はインスタンスを生成する際に呼び出され、__init__
はそのインスタンスの初期化を行います。通常、__init__
だけをオーバーライドすることが多いですが、__new__
も活用する場面があります。
pythonclass MyClass:
def __new__(cls):
print("インスタンスが作成されました")
return super().__new__(cls)
def __init__(self):
print("インスタンスが初期化されました")
obj = MyClass()
このコードでは、まず__new__
が呼ばれ、その後に__init__
が呼ばれます。
5. 不変オブジェクトのキャッシュ(インターン)
Pythonでは、一部の不変オブジェクト(整数、文字列、タプルなど)は、メモリの効率性を高めるためにキャッシュされます。たとえば、ある範囲の整数(-5から256まで)は、常に同じインスタンスを再利用します。このキャッシュ機構を利用すると、同じ値を何度も作成する手間が省け、パフォーマンスが向上します。
pythona = 256
b = 256
print(a is b) # True
a = 257
b = 257
print(a is b) # False
上記の例では、256までの整数は同じインスタンスを使用しますが、257以上の整数は異なるインスタンスとして扱われます。
6. lambda
関数の奇妙な特性
lambda
関数は、一行で書ける匿名関数です。通常の関数定義よりも簡潔に書けるため便利ですが、lambda
関数には意外な動作がいくつかあります。特に、lambda
関数内で変数がどのように扱われるかについて注意が必要です。
pythonf = lambda x: x
x = 10
print(f(x)) # 10
x = 20
print(f(x)) # 20
このように、lambda
関数は、実行時に変数の現在の値を参照することに注意が必要です。
7. __del__
メソッドの不安定さ
__del__
メソッドは、オブジェクトがガベージコレクションで破棄される際に呼ばれる「デストラクタ」として機能します。しかし、このメソッドが期待通りに動作しないことが多くあります。ガベージコレクションがいつ実行されるかは予測できず、プログラムの終了時にオブジェクトが削除されるわけではないため、リソースの解放が保証されません。
pythonclass MyClass:
def __del__(self):
print("オブジェクトが破棄されました")
obj = MyClass()
__del__
はオブジェクトが破棄されるタイミングで呼ばれるが、そのタイミングを正確に予測することはできません。
まとめ
Pythonは非常に強力で柔軟なプログラミング言語ですが、その隠れた特徴や奇妙な動作を理解しておくことが重要です。これらの「隠れた機能」を知ることで、より効率的にPythonを使いこなせるようになり、予期しないバグを防ぐことができます。