Pythonまとめ(4.その他の文・関数)
その他の文、関数
open
ファイルを開く。詳細はIOモジュールの説明を参照。
assert
assertはデバッグ支援用の構文であり、アサーションとよばれるものである。アサーションは満たされるべき条件を示すものであり、満たされない場合にAssertionErrorを発生させる。ただし最適化を有効にするとアサーションが無効になり、条件の評価は行われない。
assertは関数ではなく式である。そのためassert(...)のように()をつけない。つけてしまうとSyntaxWarningとして警告されるが、エラーにはならない。次の例はassertを関数のように使用している。これはconditionに(False,"Error")と指定したと解釈される。空でないタプルは常にTrueであるため、このassertは常に成功し、AssertionErrorが発生することはない。
assert文が長くなると折り返したくなることがある。その場合は継続文字\
バックスラッシュで次の行も継続していることを示す。
assertの利用例
assertの主な役割は、バグが発生したときの通知である。ユーザに年齢を尋ねるプログラムの例を示す。
ユーザの入力値がおかしくなることは十分予想できることであり、入力値が不正なときはメッセージを表示し処理を終了させたり、再入力を促すのが一般的な仕様である。このような仕様外の値が想定されるデータの検証でassertを使うべきではない。なぜなら最適化を有効にするとassertは削除されてしまい、検証が行われなくなるためである。また、AssertionErrorをexceptで補足するようなコードも書くべきではない。assertはあくまで開発時のデバッグに用いるのがよい。
それではassertの利用パターンの例を素因数分解する処理で考えてみる。これは試し割りにより素因数分解するものである。
このプログラムには不具合がある。
パターン1:引数の値のチェック
この関数は実引数でおかしな値(たとえば数値でない値)が渡された場合を考慮していない。この対応として以下のような選択肢がある。
- 入力値をチェックしない
- 入力値をチェックする
- 入力値のアサーションは行う
入力値をチェックしないということは、呼び出し元のプログラムが正しい値を渡すことや例外処理を行うことを期待するということだ。試しに文字列を渡してみるとTypeErrorが発生する。
このエラー出力から文字列と整数の比較が行われたことは分かるものの、この関数に不具合があったのか、この関数を呼び出す側に不具合があったのかはすぐにはわからない。
入力値をチェックするなら以下のようなコードになるだろう。この実装であれば、不正な値が渡された時点でValueErrorが送られ、正整数が期待されているのに文字列が渡されたのが分かるようになる。
次にアサーションでチェックする例を示す。
if文での入力チェックでは異常値と判定する条件を指定するのに対し、assertでは正常とみなす条件を示している点に注意が必要である。また、最適化を有効にしてプログラムを実行するとassertでのチェックは行われなくなるので、開発中はデバッグのためにチェックしてリリース時にはチェックしないということができる。不具合が無い限りはおかしな値が入ることがないときにはassertの方が適しているといえるだろう。
不具合はテストでつぶすという考えで、if文でのチェックもassertでのアサーションもしないという選択も間違いではない。開発規模や設計思想にもとづいて選ぶことが重要だ。
パターン2:処理結果のチェック
このプログラムで20を素因数分解すると正しい結果が得られない。
このような不具合の発見のために素因数分解が正しくできたか検算をassertでさせることができる。
このような検算には追加のコストがかかるが、不具合の早期発見には役に立つ。必要ないときは最適化を有効にしておけば良い。このassertの利用法は単体テストで置き換えることができるだろう。そのためassertは行わない選択も正しい。
ちなみに不具合を除いたコードは以下のようになる。
パターン3:処理途中のチェック
長い処理では途中で条件を確認することで、どの部分で異常が発生したか発見しやすくなるだろう。例として素因数分解には時間がかかることから、値を100,000未満に限定するように改変してみよう。
5行目にタイプミス(10000が誤り、正しくは100000)があるため、一部の値で素因数分解に失敗しAssertionErrorが発生する。
このassertは本来は到達しないはずの場所に書かれている。そこでassert Falseとすることでassertに到達すると必ずAssertionErrorが発生する仕組みとなっている。これはきれいなアサーションの書き方ではないが、不具合がどこに含まれているか通知するのに役立つ可能性がある。
del
オブジェクトを使用しなくなったことを明示的に示すものであり、参照を削除する。
ミュータブルなコレクションの要素に対してdelを実行すると、その要素が削除される。
global
globalを使用して大域変数を現在のコードブロックで書き換えることを宣言する。xを大域変数のxと関連付けると考えても良いかもしれない。
exec、eval
文字列をPythonスクリプトまたは式として実行する。globalsとlocalsでグローバル変数、ローカル変数を辞書形式で指定できる。
evalは式の評価結果が戻り値となる。execは常にNoneを返す。
with
ファイルをオープンするときにwithを使うと、withが終了するときに自動的にファイルがクローズされる。これはコンテキストマネージャによるものである。コンテキストマネージャを利用するとコンテキスト(≒一時的な状態・環境)の開始、終了の処理を委ねることができる。withはtry/finallyの標準的な使用方法を示すものと考えることができる。
ファイルをオープンするときの使用例を示す。with外部でfのreadlineメソッドを呼び出すとクローズされたファイルへのアクセスということでエラーが発生する。これはwithが終了したときに自動でfが閉じられるためである。
withで使用できるものはスペシャルメソッド__enter__と__exit__が定義されている。その名の通り__enter__がwithブロックに入るときの処理であり、__exit__がwithブロックを抜けるときの処理である。
また@contextmanagerデコレータを使うことで関数形式でもコンテキストマネージャを作成できる。以下のuseA関数はクラスAを管理するマネージャである。
ファイルオープン以外のコンテキストマネージャの例を示す。
用途 | 構文 | サンプル |
---|---|---|
標準出力のリダイレクト | redirect_stdout(new_target) | with redirect_stdout(io.StringIO()) as f: |
作業ディレクトリの変更 | chdir(path) |
文字列のフォーマット
変数の値を元にメッセージを出力したり、変数の値を決まった書式で出力したいことがある。その場合はフォーマットを使う。文字列のフォーマットには組み込み関数format、文字列strのメソッドstr.format()、フォーマット文字列(f-string)がある。なお、古くからある%演算子によるフォーマットもあるが、今はformatやf-stringのほうが推奨されているようだ。
str.format()では、値を挿入したい位置に{}を置き、引数で挿入する値を渡す。値を渡す方法は引数の順番で指定する方法とキーワードで指定する方法がある。
引数の順番で指定する場合で、{}の出現順と引数の順序が1対1で対応する場合、順番の指定は省略できる。これは自動的なフィールドの番号付け(automatic field numbering)とよばれる。
引数の順番を一部だけ省略することはできないが、順番での指定とキーワードでの指定を混在させることはできる。
変換フラグ
変換フラグが指定されている場合、フォーマットを行う前に文字列に変換する。変換フラグは引数の位置またはキーワード指定の次に続く。
変換フラグ | 動作 |
---|---|
!s | str()で文字列に変換 |
!r | repr()で文字列に変換 |
!a | ascii()で文字列に変換 |
書式指定子
フォーマットするときに値をどのように表示するか指定できる。この指定方法を書式指定子や書式指定文字列とよぶ。
フォーマットで使用する書式指定子の記法を以下に示す。書式指定子は変換フラグの次に続く。
各項目の意味は以下の通り。
項目 | 説明 |
---|---|
fill | 空いた桁数を埋めるときに使うパディング文字。デフォルトは空白。 |
align | <:左寄せ >:右寄せ ^:中央寄せ =:記号は左寄せ、値は右寄せ |
sign | -:負数のときだけ記号を表示(デフォルト) +:正数のときも記号を表示 (半角空白):負数のときは-、正数のときは空白 |
# | 数値を2進数や16進数で表示するときに接頭辞(0bや0x)を表示 |
0 | 0で開いた桁を埋める。fillに0、alignに=を指定したのと同じ。 |
minimumwidth | 最小の桁数。この桁数には記号や0xなどの接頭辞も含まれる。 |
grouping_option | 3桁ごとの区切り文字を指定。指定できるのはカンマ(,)またはアンダーバー(_) |
precision | 小数点以下何桁を表示するか 数値以外の場合は最大フィールドサイズ 整数の場合は無視される |
type | データをどの用に表示するか(別表参照) |
typeはデータの型によって異なる。整数で使用できるtypeを示す。
type | 意味 |
---|---|
b | 2進数 |
c | 文字 |
d | 整数(10進数) |
o | 8進数 |
x | 16進数(小文字) |
X | 16進数(大文字) |
n | ロケール依存の桁区切り付き整数(10進数) |
'' | dと同じ |
続いて少数で使用できるtypeを示す。
e | 指数表現で表示。例:1.300025e+02 |
E | 指数表現で表示。大文字のEを使用。例:1.300025E+02 |
f | 固定少数点で表示。例:130.0025 |
F | 固定小数点で表示 |
g | 一般的なフォーマット。桁数が少なければ固定小数点、桁数が多ければ指数表現。 |
G | 一般的なフォーマット。桁数が少なければ固定小数点、桁数が多ければ指数表現。指数表現のときは大文字のEを使用。 |
n | ロケール依存の小数点記号使用の一般フォーマット。 |
% | パーセント表示。 |
'' | gと同じ。 |
ロケール依存のnを指定すると桁区切りや小数点記号が変わることがある。以下にサンプルを示す。
オブジェクトの書式指定子
オブジェクトは独自の書式指定子を定義できる。たとえば日付を表すdatetimeでは以下のような指定ができる。
以下に独自のフォーマット書式を持つクラスのサンプルを示す。このサンプルはdatetimeに実際のフォーマット処理をお願いしているが、要は__format__メソッドを実装すれば良い。
docstring
クラスや関数の宣言の下でクオーテーション3つで囲む。VS Codeであれば自動補完する拡張機能を入れると良い。
docstringの記法はいくつかあるが好きなものを選べば良い。
PEP 8
PEPとはPythonの機能追加や拡張などのディスカッションを行うために作成される文書(設計書)である。これらの文書には番号が割り振られており、PEP 8とは8番のPEP文書であることを意味している。
PEP 8はPEPや標準ライブラリの実装などで使用するPythonの実装スタイルのガイダンスとなっており、一般のPython利用者も参考になる。たとえば、空白を入れる/入れない位置などのガイドがある。
このPEP8はあくまで標準ライブラリ向けのガイドであり、一般に順守しなくてはならないものではない。PEP 8にも以下のような記載がある。
Many projects have their own coding style guidelines. In the event of any conflicts, such project-specific guides take precedence for that project.
多くのプロジェクトには独自のコーディングスタイルのガイドラインがある。PEP8と独自のガイドラインが矛盾するなら、プロジェクト固有のガイドラインが優先される。
https://peps.python.org/pep-0008/