0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Python経験者が「おっ」と思える、よく使われるけど誤解されやすい機能

Posted at

Python経験者が「おっ」と思える、よく使われるけど誤解されやすい機能をテーマに、短くて深いサンプルコードをいくつか紹介します。
コードだけでなく、なぜ「なるほど」なのかの解説付きです。


✅ 1. defaultdict の「list」と「set」の違い

from collections import defaultdict

a = defaultdict(list)
b = defaultdict(set)

a['x'].append(1)
b['x'].add(1)

a['x'].append(1)
b['x'].add(1)

print(a['x'])  # => [1, 1]
print(b['x'])  # => {1}

💡 ポイント

  • defaultdict(list) は重複を許す(リスト)
  • defaultdict(set) はユニーク化される(集合)
  • 「defaultdictって何がデフォルト?」を意識できるかが経験者ポイント

✅ 2. is== の違い(Pythonオブジェクトの真髄)

a = 256
b = 256
print(a is b)  # True

a = 257
b = 257
print(a is b)  # False

💡 ポイント

  • Python は -5〜256 までの整数を **インターン(キャッシュ)**してる
  • is は同一オブジェクトかどうか(IDの比較)
  • == は値の等価比較

経験者でも a is b が常に True だと誤解しがち


✅ 3. 引数のデフォルト値がミュータブルな場合

def append_to(val, lst=[]):
    lst.append(val)
    return lst

print(append_to(1))  # => [1]
print(append_to(2))  # => [1, 2] ←えっ?

💡 ポイント

  • デフォルト引数は関数定義時に1回だけ評価される
  • つまり lst=[]再利用されるオブジェクト

🧠 回避法:

def append_to(val, lst=None):
    if lst is None:
        lst = []
    lst.append(val)
    return lst

✅ 4. ジェネレータ式とリスト内包表記の違い

squares = (x * x for x in range(5))
print(next(squares))  # => 0
print(sum(squares))   # => 30 (残りの1^2 + 2^2 + 3^2 + 4^2)

squares2 = [x * x for x in range(5)]
print(sum(squares2))  # => 30 (全要素がある)

💡 ポイント

  • (...) → ジェネレータ式(遅延評価
  • [...] → リスト内包(即時評価

✅ 5. 関数のクロージャとループ変数の罠

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

print([f() for f in funcs])  # => [2, 2, 2] ←意図と違う

💡 ポイント

  • lambda は変数 i への参照を保持している(定義時に値を閉じない)

🧠 対策(デフォルト引数を使って閉じ込める):

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

print([f() for f in funcs])  # => [0, 1, 2]

✅ 6. *args**kwargs のアンパックの裏技

def greet(greeting, name):
    print(f"{greeting}, {name}!")

args = ("Hello", "Alice")
kwargs = {"greeting": "Hi", "name": "Bob"}

greet(*args)     # => Hello, Alice!
greet(**kwargs)  # => Hi, Bob!

💡 ポイント

  • *args → 位置引数のアンパック
  • **kwargs → キーワード引数のアンパック
  • 関数合成やデコレーターで多用される

✅ 7. クラスの属性共有の罠(インスタンス vs クラス変数)

class Counter:
    count = 0

    def increment(self):
        self.count += 1

c1 = Counter()
c2 = Counter()
c1.increment()
print(c1.count)  # => 1
print(c2.count)  # => 0 ← あれ?

print(Counter.count)  # => 0 ← クラス変数はそのまま

💡 ポイント

  • self.count += 1 はインスタンスに新しく count 属性を作ってしまう
  • クラス変数を意図して変更するなら Counter.count += 1

✅ まとめ

テーマ 内容
is vs == 値の等価性とオブジェクトの同一性
defaultdict リストとセットのデフォルト動作の違い
ミュータブルなデフォルト引数 共通バグパターン、必ず None 初期化で避ける
クロージャと変数キャプチャ lambda i=i: テクニック
ジェネレータ vs リスト内包 メモリ効率と動作の違い
*args / **kwargs 関数設計の柔軟性
クラス変数の罠 Python OOPでよくある落とし穴

 

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?