VISASQ TECHBLOG - ビザスク開発ブログ VISASQ TECHBLOG - ビザスク開発ブログ

ENGINEER

  • python
  • Pythonで常に意識すべき非直感的な振る舞い

    Pythonには独特の仕様がいくつかあります。
    その中には、他のLLを習得している方ほど気が付きにくく、認識を誤りやすいものがあります。
    そこで、Pythonで頻繁に用いる仕様の中から、意外と知る機会の少ない仕様を七つ取り上げます。

    Pythonって愛嬌がありますよね

    はじめまして、寺坂です。
    ビザスクのエンジニアです。

    業務的にはビザスクのエンジニアの例に漏れず、主にPythonと{ECMA,Type}Scriptを喋ります。

    私はLinuxユーザーであることも相まって2006年頃に趣味としてPythonを触り始めたときから、
    なかなかに面倒くさいこの言語に日々愛嬌を感じずにはいられません。


    とはいえ業務で書くとなると愛嬌では済まされない部分もあります。

    ビザスクの開発チームでは、管理しているコードのうちプログラミング言語に限れば60%が、そこから{ECMA,Type}Scriptを除くと97%がPythonで実装されています。
    また、プロジェクトによってPython2.7とPython3を使い分けています。

    こうした状況下では、全てのエンジニアがPythonの仕様を正しく理解することが欠かせません。

    Pythonを好きか嫌いかの問題

    Pythonの構文・記法には他の言語と比べて独特の仕様をしている部分があります。

    インデントを強制させられる点やelif, passなどの構文が知られているかと思います。
    ところで、可読性はともかくとして、こういった見た目に関してはエンジニアの選り好みの問題に過ぎません。

    Pythonコードを意図通りに書けるか書けないかの問題

    一方で、Pythonには振る舞いの面でも一部に独特の仕様があります。

    他のオブジェクトオリエントなLLを習得しているエンジニアにとって、
    Pythonは直感的な可読性と概ね直感的な振る舞いにより、書きやすい言語です。

    しかし、直感的に書くことが出来て一見して思い通りに動くからこそ、
    一部の独特の仕様に対して誤った認識や期待のもとにコードを書き続けてしまいます。

    唐突に現れるPythonの裏切り的な振る舞い

    繰り返しますが、
    Pythonには、他の言語を習得しているほど認識を誤りやすい仕様があります。

    多くのプログラマーがそれらに対して「何だこの変な仕様は!」と叫ぶところではありますが、
    Pythonプログラマーとして言わせていただくと、それらは全てPythonの愛嬌です。
    しかし、バグの原因になることは認めざるを得ません。

    そこでこの記事では、Pythonで頻繁に用いる構文・機能の中から、
    他のLLを書き慣れているほど期待と異なる、あるいは気がつくことなく誤ったコードを書いてしまいがちなものを七つ(+二つ)取り上げます。


    免責

    • 私はプログラミングに関する難しい理論はわかりません/用語が間違っているかもしれません
    • Python2.7の振る舞いに関して書きます

    Pythonの非直感的な振る舞い群

    ※Pythonコードの冒頭に付ける # coding=utf-8 は省略しています。

    forはスコープを切らない

    ※Python3ではリスト内包に関してはスコープが切られます。

    ジェネレーターは関数スコープ

    ただし in … は除きます。PEP289 Generator Expressions / Early Binding versus Late Binding

    クラスのボディのスコープは特別

    クラスのボディ(class block)のスコープは、メソッドからの名前解決では使われません。
    (厳密には、当該class block内からの名前解決でのみ使われます)

    クラス変数は全てのインスタンスで共有される

    __init__()に書かれているか注意が必要です。Class and Instance Variables

    デフォルトパラメーター値は関数定義の際に評価される

    関数のデフォルトパラメーター値は、関数定義を実行する際にその値が評価されます。
    Why are default values shared between objects?
    つまり、評価されるのはただ一度だけで、その値が使いまわされます。

    外側のスコープの変数には代入できない

    外側のスコープで定義されている変数に代入し直すことはできません。
    ※Python3ではこの再束縛を可能にするnonlocalがあります。

    (スコープ周りをまとめると)

    ここに書かれています Execution Model / Naming and binding

    気まぐれにis

    ※以下のコードは実行環境によって異なる結果を示すことがあります。

    その他

    ときどきnon-ascii

    non-asciiを想定していないと大変なことになります。

    短い名前

    短い名前はたいていビルトイン関数や標準ライブラリに使われています。

    Pythonのコードを書く際に常に意識すべきことは

    • まずは気持ちの面でPythonの面倒な仕様は全て愛嬌だと思うようにします(個人的な見解です
      • 実際的にはIDEや各種リンターの機能で、明らかに問題になりうる箇所を検出します
    • 他のLLを書き慣れているからといってPythonを簡単に使いこなせると思わず、確認を行うまでは仕様に注意を払うようにします
    • 他のエンジニアと共有やコードレビューを行うようにします

    当たり障りのないまとめになってしまいましたが、
    実際に弊社でもコードレビューによって認識の漏れを発見し、開発チームで確認・共有を行ったことがあります。

    きっとPythonには、まだまだたくさんの愛嬌が備わっているはずです。
    わくわくしますね!