アーカイブ
はじめに
Pythonの文法は少し特殊で、JavaやGo、JavaScriptといった他言語に慣れている人ほど、思わぬ落とし穴にハマることがあります。
かくいう私も他言語からPythonに移行し、思わぬ落とし穴にハマった一人です。
本記事では、特に変数定義やスコープに関するありがちなミスを紹介していきます。
本題
変数定義やスコープに関しては、下記の2点が目立った特徴だと思っています。
- 1. ブロックスコープの概念がない
- 2. 変数宣言と代入が同一の構文である
まず、「ブロックスコープの概念がない」について具体例を示します。
with open("dummy.txt", "r") as f:
for line in f:
print(line.strip())
print(f.name) # dummy.txt -> ファイルの読込は不可だが、オブジェクト自体は参照可能
for i in range(3):
print(i)
print(i) # 2 -> ループ変数はループ終了後も参照可能(最後の値が残る)
if ok := some_func_returns_true():
print("ok")
print(ok) # True -> ウォルラス演算子で定義した変数も例外ではなく、参照可能
初めて見た時はかなり違和感があったのですが、今はこういうものだと受け入れています。
なお関数スコープはPythonにもあるため、関数内でローカル定義した変数は関数外で参照できません。
次に「変数宣言と代入が同一の構文である」について見ていきます。
「ブロックスコープの概念がない」という特徴も相まって、変数宣言したつもりが実は代入になっていた、というミスが起きやすいです。
str = some_func() # 意図しない組み込み関数strの上書き
...
x = str(some_object) # TypeError: 'str' object is not callable
v = "some value"
if ...:
v = "temp value" # 意図しない定義済み変数への代入
print(v) # temp value
v = "some value"
for k, v in {"a": 1, "b": 2}.items(): # 意図しない定義済み変数への代入
...
print(v) # 2
最後の例は実際にハマったことがあり、気付くのに少し時間がかかった記憶があります。
関数の粒度が大きかったら、解決により時間がかかったのではないかと思います。
まとめ
Pythonにおける変数定義とスコープの特徴について、他言語経験者が戸惑いやすい点に絞って紹介しました。
ブロックスコープが存在しないことや、変数宣言と代入が同一の構文であることによる落とし穴は、コードが複雑になるほど顕在化しやすいです。
文法の理解に加えて、関数の粒度を小さくしたり、変数名の命名を工夫したりすることも今回紹介したようなミスを防ぐ上では有効です。
他言語からPythonに移行する方、あるいはPythonから他言語に移行する方の参考になれば幸いです。
積極採用中!尖ったPythonエンジニアへの第一歩はこちらから
Latest articles最新の記事一覧です。