sh1’s diary

プログラミング、読んだ本、資格試験、ゲームとか私を記録するところ

Python Zen & PEP8 の検定試験(合格)

Python Zen と PEP 8 の内容を確認する無料テスト(草テスト)を受けました。ネット上だけで受けることができる簡単なもので、一応は合格後(すぐに)それっぽい PDF ファイルが送られてきます。

結果は「合格」 95/100 点

よかったです。おそらく1問5点で20問のシンプル構成です。ささっと回答すれば10分前後で終わります。

Python Zen と PEP 8 とは

最近は、かんたんなデータ処理のために python を利用する機会が増えてきたので、基本的なコードの書き方をあらためて学習したいと思いました。

とりあえず、私は coding style に興味があるほうなので、好きなところから始めたくて PEP 8 と 20 を読みました。

どちらも、自分で意訳しています。

モチベーションを持って取り組んだわけではないのですが、しっかりやっているほうじゃないかと思いました。

受験した理由

python を使う機会が多いから。

python には「There should be one-- and preferably only one --obvious way to do it.」という考え方があるので、自由度の高い言語なんだけど、どういうコードがいいコードか、という方針や指針があります。

いい感じの書き方は、少しずつでもいいから学習したほうがいい。

勉強に使ったもの

公式の PEP を参照した。PEP = Python Enhancement Proposal で「python の(機能)拡張提案」くらいの意味だと思う。

あと、参考書も見直して slice などの仕様をテストするなどしました。

学習期間

  • PEP 20:2025/05/23 ~ 2025/05/26
  • PEP 8: 2025/05/26 ~ 2025/05/29

毎日1H~3H程度で、合計で15H前後。試験難易度からすると、かなり多いと思います。

英語が得意なわけではないので、単純に翻訳が難しかったり、よくわからない方針/指摘の内容はテストしたりサンプルコードを作ろうとすると、時間がかかりました。 とくに PEP 8 は、かなりサンプルで実行できるコードを追加しています。

費用

0 円

特に今回のために買い物をしたりはしていません。テスト費用もかかりませんでした。

参考

Python - PEP20 The Zen of Python

PEP8 について学習しました。このテキストの内容は PEP8 の正確な「翻訳」ではなく、自分が内容を理解するために(自分用に)調整した「意訳」版です。

原文の PEP8 では、文章だけで説明される規則に対して、独自のサンプルを追加しているものもあるので注意。

日本語版でもよいのかもしれないのですが、python は、詳しくないこともあって、どうも意味がよくわからないところがあって、自分で調べて勉強することにしたものです。

Introduction(はじめに)

このドキュメントでは、Python の main distribution の標準ライブラリを構成する Python のコードのコーディング規約 (coding conventions) を示します。Python の C 実装における C コードのスタイルガイドラインについては PEP7 を参照すること。

このドキュメントと PEP257 (Docstring Conventions) は Guido 氏の作ったオリジナルの Python Style Guide に Barry 氏のstyle guideをいくつか加えた内容になっています。

これらの style guide は、時間の経過と共に進化します。言語自体の変更によって、過去の規則が廃止されたり、追加の規則があります。

多くのプロジェクトでは、独自の coding style guideline を持っています。矛盾が生じたときは、そのプロジェクト固有のガイドを採用してください。

A Foolish Consistency is the Hobgoblin of Little Minds(愚かな一貫性は小さな心のホブゴブリン)

Guide 氏の重要な洞察のひとつは、コードは書かれることよりも読まれることのほうがずっと多いということです。ここで提供される guideline は、コードの読みやすさを改善し、python のコードの広い範囲で一貫性を持たせることを目的としています。PEP20 が言うのところの "Readability counts" です。

style guilde とは、一貫性のことです。この style guide と共に一貫性 (consistency) は重要です。プロジェクトの中での一貫性はもっと重要です。ひとつの module や function の中での一貫性がもっとも重要です。

しかしながら、矛盾が生じることを知っておくーーなんらかの style guide の推奨することが提供できないことがある。迷ったときは、あなたの best judgment をします。他の例を見て、なにがベストか決めてください。そして、躊躇わずに聞いてみること!

特に注意すべき点:この PEP に準拠するためだけに、後方互換性を壊さないこと!

特定の guideline を無視するいくつかの理由もある:

  1. このガイドラインを適用することで、この PEP に従ったコードに読みなれている人であっても、コードが読みにくくなるケース
  2. (歴史的な理由から)guideline を破っているまわりのコードとの整合性を取るケースーーただし、それは他人の書いたコードを改善する機会でもある(本物の XP = extreme programming スタイルでいこう)
  3. もしも、そのコードが guideline を導入する以前に書かれたものであり、また、そのコードを変更する他の理由がないケース(=それを今すぐに直す必要はない)
  4. styleguide が推奨する機能をサポートしていない古いバージョンの python との互換性を保つ必要があるケース

Code Lay-out

Indentation

indent は、レベルごとに4つの space " " を使うこと。

改行されてしまう(複数行の)コードは ()[]{} の括弧を使って、複数行にして、要素を縦に揃える、または、ぶら下がりインデント hanging indent を使って整列させるべきです。ぶら下がりインデントを使うときは、次の点に気をつけること;最初の行には引数を書かず、継続行であることが明確に区別するため、さらに字下げ indent をすること:

footnote: ぶら下がりインデント hanging indent とは、段落内のすべての行が最初の行を除いて indent される style です。python での、ぶら下がりインデントという用語は、括弧付き文 parenthesized statement において、「1.開き括弧で行の終わりにあること」、「2.次の行から閉じ括弧までを indent する」この style のことを指します。

# Correct:

# 垂直にそろえている
foo = long_function_name(var_one, var_two,
                         var_three, var_four)

# ぶら下がりインデント:4-space 分だけ字下げ
def long_function_name(
        var_one, var_two, var_three,
        var_four):
    print(var_one)

# 呼び出しのときも、ぶら下がりインデントで OK
foo = long_function_name(
    var_one, var_two,
    var_three, var_four)
# Wrong:

# ぶら下がりインデントなのに引数を1行目に入れてるから NG
foo = long_function_name(var_one, var_two,
    var_three, var_four)

# 継続する行のインデントが浅くてわかりづらいから NG
# 本当は 4-space のインデントがあったほうがいい
def long_function_name(
    var_one, var_two, var_three,
    var_four):
    print(var_one)

継続行における 4-space のルールは必須ではありません。

任意の例:

# ぶら下がりインデントは4つ以外も可能な例
foo = long_function_name(
  var_one, var_two,
  var_three, var_four)

if 文の条件部分が長くて複数行にまたがるとき、if という2文字のキーワードとひとつの (スペース)そして ( を組み合わせると if ( になってしまい、自然と 4-space されるため、視覚的に紛らわしい状態を生む恐れがあります。

PEP8 では、そうした条件式と nest された code block をさらに視覚的に区別する方法(あるいは、区別するかどうか)については、明確な方針を示していません。このようなときに許容される選択肢には、次のようなものがありますが、これに限るものではありません:

# 追加のインデントなしのパターン
if (this_is_one_thing and
    that_is_another_thing):
    do_something()

# コメントを追加して見た目で判断しやすくするパターン
if (this_is_one_thing and
    that_is_another_thing):
    # Since both conditions are true, we can frobnicate.
    do_something()

# 条件式の継続行をさらに字下げインデントするパターン
if (this_is_one_thing
        and that_is_another_thing):
    do_something()

(後述の binary operators の前後で break するかどうかの議論も参考にすること)

{} [] () の括弧は、複数行にまたがるとき list の最終行の文字に揃えることができます。次のような書き方になる:

my_list = [
    1, 2, 3,
    4, 5, 6,
    ] # リストの要素の開始位置にあわせる
result = some_function_that_takes_arguments(
    'a', 'b', 'c',
    'd', 'e', 'f',
    )

あるいは、複数行を開始する行の最初の文字の下に揃えることもできます:

my_list = [
    1, 2, 3,
    4, 5, 6,
] # 最後の行が開始する行にあわせられる
result = some_function_that_takes_arguments(
    'a', 'b', 'c',
    'd', 'e', 'f',
)

Tabs or Spaces?(インデントは tab 文字かスペースの文字か)

space は望ましい indent の方法です。tab \t はすでに tab 文字で indent されてしまっている code との一貫性を保つためだけに使うべきです。

python では tab と space を混ぜて indent をすることを禁止しています。

Maximum Line Length(1行の最大文字数)

1行の最大文字数は 79 文字に制限します。(プログラムにおける構文などの)構造的な制約がすくないテキストブロック (docstring や comment) の長さは 72 文字に制限するべきです。

editor の window 幅の要件を制限することで、複数ファイルを並べて開くことができるようになります。(異なる)ふたつのバージョンのコードを、左右に並べて表示する code review のツールを使うときにも適しています。

多くの tool の自動折り返し機能は、コードの視覚的な構造を乱してしまうので、コードを理解しづらくします。ここまでの制限ルールは 80 文字の幅に設定された editor で折り返しが発生しないようにするものです。79文字にする理由は、80文字目に marker glyph として の記号を加えて入れる機能があるので、(最後の1文字が隠れることになるのを)それを避けるためです。また、一部の web tool では、自動折り返し機能自体が無いこともあります。

チームよっては、行の文字数が長いことを好むこともあります。ここまでの指摘を許容することができるチームなら、行の長さの制限を 99 文字まで増やしてもよいです。しかし、docstring と comment は 72 文字で折り返してください。

python の standard library は保守的です。行を 79 文字に制限することを要求します。docstring/comment は 72 文字です。

長い行を折り返すときは括弧 ()[]{} の中で python の暗黙的な行継続を利用します。長い式は括弧で囲むことで複数の行に分割できます。この方法は backslash \ を使った継続よりも好ましいです。

backslash \ を使うほうが適切なケースもあります。例えば、python 3.10 よりも以前は with を使った長いコードでは backslash の使用が許容されていました。

with open('/path/to/some/file/you/want/to/read') as file_1, \
     open('/path/to/some/file/being/written', 'w') as file_2:
    file_2.write(file_1.read())
# python 3.10 以降ではそもそも問題ない
with (
    open("file1") as f1,
    open("file2") as f2
):

(このような複数行の with statement の indent については、複数行の if statement に関する話を参考にすること)

もうひとつのケースは assert statement です。続きの行を適切に indent してください。

(以下は勝手に追記している)おそらく:

# Correct:
assert (
    some_long_condition and
    another_condition
)

assert some_long_condition and \
       another_condition

assert (
    user.is_authenticated and
    user.has_permission("edit")
), "User must be authenticated and have edit permission"
# Wrong:
assert some_long_condition and
another_condition  # どこまでが条件か不明瞭

Should a Line Break Before or After a Binary Operator?(binary operator の前後で改行すべきか?)

何十年もの間 binary operator の後で改行することが推奨されていました。しかし、演算子は画面上の異なる位置に散らばる傾向があり、各演算子は operand から離れて前の行に移動しています。

なので、どの項目が加算されて、どの項目が減算されたのかを見分けるためには、目は面倒な動作をすることになる。

# Wrong:
# 演算子は operands から離れているパターン
income = (gross_wages +
          taxable_interest +
          (dividends - qualified_dividends) -
          ira_deduction -
          student_loan_interest)

この読みやすさの問題を解決するために、数学者と出版社は反対の慣例に従っています。Donald Knuth は彼の Computers and Typesetting シリーズでルールを説明しています:"段落内の(インライン)数式は binary operator の後で改行され、ブロック形式の (displayed) 数式 は operator の前で改行される"(Tex の話)

数学の伝統に従うことで、通常は読みやすいコードになります:

# Correct:
# 演算子と operand のマッチングが簡単
income = (gross_wages
          + taxable_interest
          + (dividends - qualified_dividends)
          - ira_deduction
          - student_loan_interest)

python のコードでは、binary operatorの前後で行を折り返すことが許容されています。ただし、locally(≒ そのscope, block、module, function のような狭い範囲)において style が一貫している限りです。

Blank Lines(空白の行)

top-level の関数とクラス定義を2行の空白行で囲むこと。

def func1():
    pass


class MyClass:
    pass


# ...

class 内の method 定義は1行の空白行で囲むこと。

class MyClass:
    def method1(self):
        pass

    def method2(self):
        pass

# ...

関連する関数のグループを区切るために、余分な空白行を(sparingly ≒ 控えめに)使用してもよい。空白行は、関連する1行だけの関数が連続するときは、省略してもよい。(例えば、ダミー実装のセット)

def load_user_data():
    # ユーザーデータを読み込む
    pass

def validate_user_data():
    # ユーザーデータを検証する
    pass


def load_product_data():
    # 商品データを読み込む
    pass

def validate_product_data():
    # 商品データを検証する
    pass


def save_results():
    # 処理結果を保存する
    pass


def noop1(): pass
def noop2(): pass
def noop3(): pass

関数内の空白行は、論理的な section を示すため控えめに使用すること。

python では control+L = home feed の文字も空白文字として受け付けています。多くのツールでは、区切り文字として認識するので file 内の大きな section を区切りたいときに使うことができます。Note, 一部の editor や web 上の code reviewer では control-L を home feed として認識せずに別の記号を表示することがある。(互換性に注意する必要があるということ)

空白行は、意味のある区切りとして使うというあたりが PEP8 の sparingly ≒ 控えめに、の意図だと思います。

Source File Encoding

python の core distribution のコードは UTF-8 を使うべきです。encoding declaration は持つべきではありません。

encodiong declaration = XML だと <?xml version="1.0" encoding="UTF-8"?> のようなものをファイルの先頭に記述すること。pythonencoding declaration = エンコード宣言ができる。

標準ライブラリの中では UTF-8 以外の encoding はテスト目的でのみ使用するべきです。非 ASCII 文字の使用は控えめにして、できれば地名や人名などに限るべきです。データとして非 ASCII 文字を使うときは z̯̯͡a̧͎̺l̡͓̫g̹̲o̡̼̘ のようなノイズの多い Unicode 文字や BOM の使用は避けること。

python の標準ライブラリの中では識別子(変数名や関数名など)は ASCII のみで書くことは MUST です。そして、できるだけわかりやすい英単語の名前を使うのは SHOULD です。

世界中に利用者のいるオープンソースのプロジェクトは、同様の方針を採用することが推奨されます。

Imports

  • Import は通常、別の行にすべきです:
# Correct:
import os
import sys
# Wrong:
import sys, os

とは言っても、このようにしても OK です(同じモジュールから複数の機能を import する例):

# Correct:
from subprocess import Popen, PIPE

import は必ずファイルの一番最初に書かれます。モジュールに対する comment や docstring のすぐ後であり、module の globals や constaints の前です。

# モジュールに関するコメント(任意)
# This module handles user authentication management.

# import
import os
import sys
from datetime import datetime, timedelta
from typing import Optional

# モジュール内で使う定数やグローバル変数
SESSION_TIMEOUT = 3600
DEBUG_MODE = True

# その後に関数やクラス定義
def sample(username: str, password: str) -> bool:

import は以下の順序でグループ化するべきです:

  1. 標準ライブラリ
  2. 関連するサードパーティ製ライブラリ
  3. 自作アプリケーション、ライブラリの local module

各グループの間には、ひとつの空白行を入れて区切るべきです。

  • absolute import が推奨されます。import の仕組みが誤って構成されているとき(例えば、パッケージ内のディレクトリが(紛らわしい)sys.path に入っているようなケース)でも、適切に動作する(または、少なくとも、わかりやすいエラーメッセージを与えてくれる)傾向にあるからです:
import mypkg.sibling
from mypkg import sibling
from mypkg.sibling import example

しかしながら、明示的な relative import も条件つきでアリです。特に、複雑なパッケージ構成において absolute import だと不必要に冗長になるようなケースだと有効だろう:

from . import sibling
from .sibling import example

標準ライブラリのコードでは、複雑なパッケージ構成そのものを避けて、常に absolute import を使うべきです。

クラスを含むモジュールからクラスを import するときは、つぎの書き方をすると OK です:

from myclass import MyClass
from foo.bar.yourclass import YourClass

もしも、MyClassYourClass の名前が衝突してしまうときは module ごと explicitly ≒ 明示的にします:

import myclass
import foo.bar.yourclass

そして、コードの中では myclass.Myckassfoo.bar.yourclass.YourClass のようにして使います。(あくまで、名前が重複するなどの問題があるときの話)

wildcard import from <module> import * は避けるべきです。その理由は、import してきた機能は、どの名前空間に存在していのかわからなくなって、コードを読む人もツールも混乱します。

ただし wildcard import に正しさのある使い方がひとつだけあります。それは、internal interface を public API として再公開するために使う場合です。(例えば、ある interface の python の実装を、任意の高速化させたモジュールが存在するときに、それで上書きするようなケースです。どの定義が上書きされるのか事前には、わからない場合に限って import * が役に立つことがあります)

# C拡張モジュール(高速化)を使えたらそれを優先して取り込む
try:
    from ._accelerator import *
except ImportError:
    from ._fallback import *

このように名前を再公開するときも、public & internal interface の扱いに関する guideline は以前として適用されます。

Module Level Dunder Names(モジュールレベルの dunder の名前)

dunder name とは、名前の先頭と末尾に underscore を2つずつ持つ __name__ のような特別な名前のことです。これをどこに書くべきか、ということです。dunder = double underscore のこと。口語的なスラングでダンダーが近い発音になる。

例えば __all__, __author__, __version__ などはモジュールの docstring の直後に挿入し import の前に配置するべきです。__future__ からの import は例外で、python は docstring を除いて、コードの一番最初に future-import が現れることが義務付けられています。

# PEP257 形式の docstrings
"""This is the example module.

This module does stuff.
"""

# future インポート(使う場合は、一番最初に書く)
from __future__ import barry_as_FLUFL

# dunder を定義
__all__ = ['a', 'b', 'c']
__version__ = '0.1'
__author__ = 'Cardinal Biggles'

# 通常のインポートはその後
import os
import sys

String Quotes(文字列の quote 処理)

python では single-quote 'string' と double-quote "string" の文字列は同じです。PEP8 はどちらを使うべきかについて、特に推奨していません。ルールを決めたら、それを一貫して使ってください。

ただし、文字列の中に single-quote や double-quote が含まれる場合は、escape \ を避けるために、反対の quote を使います。このほうが読みやすいからです。

# Correct:
quote = "I'm happy." # single-quote があるから double-quote
quote = 'He said, "Hi."' # double-quote があるから single-quote
# Wrong:
quote = 'I\'m happy.'
quote = "He said, \"Hi.\""

triple-quote を表現したいときは、PEP257 docsting 規約と一致するように常に double-quote """ を使ってください。

"""This is a docstring."""

Whitespace in Expressions and Statements(expression や statement における空白)

Pet Peeves(気にくわないこと)

次のような状況では、余計な余白の space を避けること:

# Correct:
spam(ham[1], {eggs: 2})
# Wrong:
spam( ham[ 1 ], { eggs: 2 } )

末尾のカンマ , と括弧の間も、余計な余白の space を避けること:

# Correct:
foo = (0,)
# Wrong:
bar = (0, )

カンマ ,セミコロン ,、コロン : の直前も、余計な余白の space を避けること:

# Correct:
if x == 4: print(x, y); x, y = y, x
# Wrong:
if x == 4 : print(x , y) ; x , y = y , x

しかしながら、slice 構文では、a[1:9] のようにコロン : は binary operator のように振る舞うため、その前後に等しい数の空白を置くべきです。(優先順位が最も低い演算子と扱われる)extended slice(拡張 slice 構文)では、a[1 : 9 : 3] のように両方のコロン : に同じ数の空白を適用するべきです。

例外:slice のパラメーターが省略されているとき a[1:] は、空白も省略。

# Correct:
ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:]
ham[lower:upper], ham[lower:upper:], ham[lower::step]
ham[lower+offset : upper+offset]
ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
ham[lower + offset : upper + offset]
# Wrong:
ham[lower + offset:upper + offset]
ham[1: 9], ham[1 :9], ham[1:9 :3]
ham[lower : : step]
ham[ : upper]

関数の引数リストを開始する括弧の直前は、余計な space を避けること:

# Correct:
spam(1)
# Wrong:
spam (1)

indexing や slicing の括弧 [] の前は、余計な space を避けること:

# Correct:
dct['key'] = lst[index]
# Wrong:
dct ['key'] = lst [index]

代入演算子 = の前後は、(他行の演算子と整列させるために)空白を入れすぎないこと:

# Correct:
x = 1
y = 2
long_variable = 3
# Wrong:
x             = 1
y             = 2
long_variable = 3

Other Recommendations(そのほかの推奨)

末尾の空白 (trailing whitespace) は避けてください。なぜなら、たいていは目に見えないし、混乱を引き起こします。例えば、バックスラッシュ \ の後に space と改行が続いていても、それは改行記号として機能しません。

editor によっては、空白を保存しないものもあるし、(CPython 自身を含む)多くのプロジェクトでは空白を拒否する pre-commit hook(Git などで commit 前に自動で実行されるコードチェック・整形のスクリプトのこと)がある。

binary operator の前後には常に空白をひとつずつ入れること。

  • assignment =
  • augmented assignment += -= etc.
  • comparison == < > != <= >= in not in is is not
  • boolean and or not

優先順位の異なる演算子を組み合わせる場合は、優先順位の低いほうの演算子の前後に空白を入れることを考慮します。(空白は)あなた自身の判断で使ってもよいのですが、空白は必ず両側に同じ数(通常1個)を入れること。

# Correct:
i = i + 1
submitted += 1
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)
# Wrong:
i=i+1
submitted +=1
x = x * 2 - 1
hypot2 = x * x + y * y
c = (a + b) * (a - b)

優先度の低いほうの演算子の前後にだけ space を入れるため x*x + y*y のように + の前後にだけ space がある。

(あなた自身の判断で space を使えともあるので)多少のスタイルの揺れは許されると思います。数式が長くなると、このルールの適用が難しいことがあるかもしれません。(長い数式は複数行に分割することも示されている)

関数の annotation は、コロン : のルールを使用します。戻り値の指定 -> を使うなら、その前後に space -> を入れること。(詳細は後述の「Function Annotations」を参照)

# Correct:
def munge(input: AnyStr): ...
def munge() -> PosInt: ...
# Wrong:
def munge(input:AnyStr): ...
def munge()->PosInt: ...

キーワード引数を示す場合や、annotation のない関数パラメータにデフォルト値を指定するときは = の前後に空白を入れないこと:

# Correct:
def complex(real, imag=0.0):
    return magic(r=real, i=imag)
# Wrong:
def complex(real, imag = 0.0):
    return magic(r = real, i = imag)

ただし、引数 annotation とデフォルト値を組み合わせるときは = の前後に空白を使用すること:

# Correct:
def munge(sep: AnyStr = None): ...
def munge(input: AnyStr, sep: AnyStr = None, limit=1000): ...
# Wrong:
def munge(input: AnyStr=None): ...
def munge(input: AnyStr, limit = 1000): ...

複合 statement(同じ行に複数の statement を書くこと)は、一般的に推奨されません:

# Correct:
if foo == 'blah':
    do_blah_thing()
do_one()
do_two()
do_three()
# Wrong (Rather not = しないほうがいいと思う):
if foo == 'blah': do_blah_thing()
do_one(); do_two(); do_three()

if, for, while を同じ行に書いてもいいことがあります。しかし、複数の節 multi-clause を持つコードのところでは、するべきではないです。また、長いコードを無理に折り返すことも避けること!

# Wrong (Rather not = しないほうがいいと思う):
if foo == 'blah': do_blah_thing()
for x in lst: total += x
while t < 10: t = delay()
# Wrong (Definitely not = 絶対に違う):
if foo == 'blah': do_blah_thing()
else: do_non_blah_thing()

try: something()
finally: cleanup()

do_one(); do_two(); do_three(long, argument,
                             list, like, this)

if foo == 'blah': one(); two(); three()

When to Use Trailing Commas(末尾カンマを使うとき)

末尾カンマが冗長(不要)でも、バージョン管理の仕組みを使用するときは、有用になることがあります。値、引数、import の項目が(将来的に)増えるかもしれないときが該当します。

書き方のパターンとしては、値を1行ずつに書き、末尾カンマを加えて、閉じる括弧 (parenthesis/bracket/brace) と同じ行にカンマを置くのは非推奨。(ただし、singleton tuple を除く)

# Correct:
FILES = [
    'setup.cfg',
    'tox.ini',
    ]
initialize(FILES,
           error=True,
           )
# Wrong:
FILES = ['setup.cfg', 'tox.ini',]
initialize(FILES, error=True,)
tuple のような場合は ('tox.ini',) にしないと tuple にならない。 ('tox.ini') では値になる。

Comments

コードと矛盾するコメントは、コメントが無いことよりも悪いものです。コードが変更された時は、常にコメントを最新の状態に保つことを優先すること!

コメントは完全な文章でなければならないです。最初の文字は大文字で始めます。(識別子の大文字小文字は、絶対に変えないこと!)

block comment は、通常だと完全な文章をひとつ以上の段落で構成します。それぞれの文はピリオドで終わるようにします。

複数の文で構成されるコメントでは、文の末尾のピリオドの後に1 or 2の空白を使用する必要があります。

英語を母国語としない国の python corder のコメント:そのコードがあなたの言語を母国語としない人に読まれることは絶対にないことを 120% 確信していない限り、コメントは英語で書いてください。

最後の指摘は unless x never be read x by people who don't speak your language で否定が3つもある文章構造。単に「英語以外で書くな」と示すと言葉として強すぎるので、とても強く制限をかけたい言い回しになっている。120%の確信なんてありえないから、英語で書いてねと読んでおけばいい気がする。(もしくは、あなただけのプライベートなコードなら好きにしたらいい、とも読める)

Block Comments

block comment は一般的に、それに続くコードの一部、または、すべてに適用されます。そして、(コメントを挿入する位置の)コードと同じ位置(レベル)にインデントします。

block comment の各行は # と、ひとつの space # から始まります。(コメント内のテキストが、引用、サンプルコードなどの理由で特別なインデントのある場合は別)

block comment 内の段落は "#" だけの1行で区切る。

# This function initializes the database connection
# and prepares the schema if necessary.
#
# It should be called once at the beginning of the program,
# before any database access occurs.

Inline Comments

inline comment は控えめに使うこと。

inline comment とは、statement と同じ行にあるコメントのことです。inline comment は statement から少なくとも2つの space で区切らなければいけません。inline comment は # で始めてください。

(コードの内容が)明らかなことを述べるのであれば inline comment は邪魔になります。以下のようなものは書かないこと:

# コードを見れば明らかなことは書かないこと
x = x + 1                 # Increment x

しかし、役に立つ inline comment もある:

# なぜ加算をしているのかを説明している(コード理解の補足)
x = x + 1                 # Compensate for border

Compensate for border の意味は、あまり気にする必要はないと思うけど「border の値に+1の補正をする」ようなことを伝えたいように思います。日本語の PEP8 だと「境目を補う」というテキストでした。(私の理解だと)x は border になるし、加算の意味は(自動詞の)compensate になったし、情報を出しているので人狼ではない善良な市民の行動に見えます。(これだと関数に対するコメントでよいことも多いはずなので、よく使うものではなくて「控えめに」必要があるときだけ使う)

Documentation Strings

documentation strings(別名: docstrings)を書くための規則は PEP257 に示されています。(are immortalized = 永遠に記録されている)

すべての public module, function, class, method には docstring を書くこと。非公開の method には必要ないけど、処理の内容を説明するコメントは書くべきで def の行の直後に書く。

def func():
    """" func comment... """

PEP257 には docstring の規則が書かれています。最も重要なことは、複数行の docstring の最後を示す """ は、単独の1行にして書くこと。

"""Return a foobang

Optional plotz says to frobnicate the bizbaz first.
"""
# ↑ docstring の末尾は `"""` 以外になにも書かない

1行だけの docstring なら、全部同じ行に置いてください:

"""Return an ex-parrot."""

Naming Conventions(命名規則

Python命名規則はすこしの混乱がある (a bit of a mess) ため、命名規則に完全な一貫性を取ることはできないでしょう。しかしながら、現在の推奨される命名標準 (naming standards) を以下に示します。

新しい module や package(third-party の framework を含む)は、示す命名標準に従って書かれるべきですが、既存のライブラリが(すでに)異なる style を持っているときは、内部的な一貫性を優先すべきです。

Overriding Principle(他よりも優先される原則)

API の公開部分としてユーザーに見える(見られる)名前は、実装ではなくて用途に即した命名規則に従うべきです。

# Correct: 使う側の目的に沿った名前
def calculate_checksum(): ...

# Wrong: 実装の都合に合わせた名前
def hash_md5_internal_func(): ...

Descriptive: Naming Styles(叙述 or 説明的な命名の style)

naming style には、いろいろなものがある。その名前が何に使われているのかとは関係なく、どのような naming style が使われているのかを認識することは役に立ちます。

一般的には、以下のような naming style が区別されて存在している:

  • b(1文字だけの小文字)
  • B(1文字だけの大文字)
  • lowercase
  • lower_case_with_underscores
  • UPPERCASE
  • UPPER_CASE_WITH_UNDERSCORES
  • CapitalizedWords(CapWords, CamelCase, StudlyCaps とも言う。文字が凸凹している見た目からついた名前)
    • Note: CapWords で 頭字語 (acronym) を使うときは、頭字語のすべての文字を大文字にします。(なので HttpServerError よりも HTTPServerError がよい)
  • mixedCase (CapitalizedWords との違いは最初の文字が小文字)
  • Capitalized_Words_With_Underscores(醜い)

また、関連する名前をグループ化するために、短い unique prefix を使用する style もあります。これは python では、あまり使われませんが、念のため記載しておきます。

例えば、os.stat() 関数は、伝統的に st_mode, st_size, st_mtime などの名前を持った tuple を返却します。(これは POSIX system call 構造体の field との対応を強調するためのもので POSIX system call 構造体に慣れているプログラマにとって、役に立つ名前になります)

(一例ですが、UNIX 系 OS で広く使われている)X11 ライブラリでは、すべての public 関数の頭に "X" をつけています。python では、この style は一般的に不要だと考えられています。なぜなら、attribute と method の名前の前には object が付き、function の名前の前には module の名前が付くからです。(それぞれ object と module が prefix として利用される)

加えて、先頭/末尾の underscore を使用する以下の特殊な形式が認識されています(これらは、一般的にどのような規則とも組み合わせることができる):

  • _single_leading_underscore
    • 弱い内部使用 “internal use” の indicator(標識)です。
    • 例:from M import * では underscore の付く名前は import されません。(=なので内部使用である)
  • single_trailing_underscore_
    • python のキーワードとの衝突を避けるために使用されます。
    • 例:tkinter.Toplevel(master, class_='ClassName')
  • __double_leading_underscore
    • class attribute の命名に使用すると名前の mangling が発生します。
    • 例:__booFooBar クラスの中では _FooBar__boo になる。
  • double_leading_and_trailing_underscore
    • ユーザー制御の名前空間に存在する "masic" object or attribute の名前。
    • 例:__init__, __import__, __file__ のような名前は決して使ってはいけないです。(公式に)ドキュメントにある名前のものだけを利用すること。(=なので勝手に独自の dunder を定義してはいけない)

Prescriptive: Naming Conventions(模範的な命名規則

Names to Avoid(避ける名前)

小文字の l(エル)大文字の O(オー), I(アイ)を1文字の変数名として使用しないこと。font によっては、これらの文字は見分けがつかなくなります。どうしても l を使いたいときは L を使ってください。

ASCII Compatibility(ASCII 互換性)

標準ライブラリで使用される識別子は、PEP3131 の policy の section に記述されているように ASCII 互換でなければならない。

つまり、変数名や関数名は英数字 (ASCII) の文字のみを使うこと。

# Wrong
# 日本語の関数の名前
def データを取得する():
    return "data"

# 変数名にギリシャ文字を使わない
π = 3.14

Package and Module Names(package と module の名前)

module の名前は短くして、すべて小文字 (all-lowercase) にすること。読みやすくなるのであれば、underscore を使ってもよいです。python package も underscore の使用は推奨されません。しかしながら、すべて小文字 all-lowercase の短い名前を持つべきです。

C or C++ の拡張 module で、より高レベルの(例:more オブジェクト指向な)interface を提供する python module が付属しているときは、C/C++ module には underscore が付きます。(例:_socket

# C/C++ の拡張 module
import _socket

# python の module
import socket

Class Names

class の名前は CapWords 記法を使うべきです。

class MyClass:
    pass

class UserProfile:
    pass

class を function のように使用することが document 化されていて、実際にも主に関数のように呼び出して使う (callable) 場合は、function の命名規則を class の規則の代わりに使ってもよいです。

class make_widget:
    def __call__(self, name):
        return f"widget: {name}"

# 関数のように使用する
w = make_widget()
w("button")

Note: builtin の名前には別の規則があることに注意すること:多くの builtin の名前は単語1~2語です。CapWords が使われるのは exception や builtin 定数の名前のみです。

種類 命名 style
builtin 関数 小文字1~2語 len, open, isinstance
builtin 例外 CapWords ValueError, TypeError
builtin 定数 CapWords None, True, False

Type Variable Names(型変数の名前)

PEP484 で導入された type variable は CapWords を使用して、短い名前を好みます。(例:T, AnyStr, Num

convariant or contravariant の振る舞いを宣言する type variable の使い方では、それぞれ _co_contra の suffix をつけることを推奨します。

from typing import TypeVar

VT_co = TypeVar('VT_co', covariant=True)
KT_contra = TypeVar('KT_contra', contravariant=True)

convariant のサンプルは、以下:

from typing import TypeVar, Generic

class Animal:
    def speak(self):
        print("I'm an animal.")

class Dog(Animal):
    def speak(self):
        print("Woof!")

T_co = TypeVar('T_co', covariant=True)

class Box(Generic[T_co]):
    def __init__(self, value: T_co):
        self.value = value

    def get(self) -> T_co:
        return self.value

# Box[Dog] は Box[Animal] として扱える(共変)
dog_box = Box(Dog())
animal_box: Box[Animal] = dog_box

# 確認
animal_box.get().speak()  # Woof!

Exception Names

exception は class であるべきなので、class の命名規則が適用されます。ただし、(もしも、実際に "Error" なら)exception の名前には "Error" という語尾を付けるべきです。

C# だと語尾にほぼすべて ~Exception のような名前になっています。python では語尾に Error を付けないことがあるということ。たとえば C#OperationCanceledException に相当する例外は raise KeyboardInterrupt なので、例外処理なんだけど、異常のようには(いくらか)見えづらい。たしかに C# の例外は「悪」に見えるのかもしれないと思ったが、例外は「本当に例外的なことが起きたとき」に使うものという思想があるはず。

python だと raise ~ の形を制御 flow にも使う柔軟な考え方になっていると思う。だけど、どちらもパフォーマンス面ではコストが高いので仕様を理解して柔軟に利用すること。

Global Variable Names(グローバル変数の名前)

(※グローバル変数は、ひとつの module の中でだけで利用するようにします)命名規則は、function と同じです。

from M import * の使い方を想定して設計された module は、グローバル変数の export を防ぐために __all__ のメカニズムを使うべきです。また、従来の方法である underscore を(名前の最初に)付けることで non-public を示す方法も使えます。

# module_a.py
__all__ = ['func1']  # これで func1 以外は * でインポートされない

def func1():
    pass

def func2():
    pass

_my_global = 42
from module_a import *
# func1 は使えるが、func2 や _my_global はインポートされない

Function and Variable Names

function の名前は、小文字 lowercase で書きます。読みやすくするため、必要に応じて underscore で区切ってください。

変数 variable の名前も同じ規則に従います。

mixedCase は、すでにその style が使われるようなケース(例:threading.py)に限ってください。後方互換性を保つために認められます。

Function and Method Arguments

  • instance method の第一引数は、常に self を使うこと。
  • class method の第一引数は、常に cls を使うこと。

もしも function の引数名が予約語と衝突するなら、一般的には省略したような語や崩れた語 spelling corruption を使用するよりも末尾に underscore をひとつ追加したほうがよい。

# Correct
def foo(class_):  # class_ は一目で意図がわかる
    pass
# Wrong
def foo(clss):  # clss は誤解を生むかもしれない
    pass

したがって、 class_ のほうが clss よりも優れています。(同義語 synonym を使用することで、このような衝突を避けることができるかも)

Method Names and Instance Variables(メソッド名とインスタンス変数)

function の命名規則:読みやすくするために、必要に応じて underscore で単語を区切った小文字を使うこと。

underscore を(名前の最初に)ひとつ使用するのは non-public method と instance 変数のみです。

subclass との衝突を避けるために python の mangling ルールを使用してください。(名前の最初に __ のように underscore を2つ付ける)

python はこれらの名前を class の名前を使いつつ「名前の書き換え」name mangling をします:もし Foo クラスに __a という名前の attribute があっても Foo.__a ではアクセスできません。(しつこいユーザーは Foo.Foo__a の記述でアクセスできるけど)

一般的に、二重の underscore は、subclass 化をする class の属性と名前の衝突を避けるためだけに使用するべきです。

Note: __names の使用については、議論が存在します。(後述のとおり)

ここでの mangling は名前を「変形して隠すこと」です。subclass にも使ってほしくないということにもなります。具体的には underscore の部分が該当していて、内部的には Foo.Foo__a の部分で衝突を防ぐ。

class Base:
    def __init__(self):
        self.__data = 123  # Base の __data

class Sub(Base):
    def __init__(self):
        super().__init__()
        self.__data = 999  # Sub の __data

b = Sub()
print(dir(b))  # _Base__data と _Sub__data がある

Constants(定数)

定数 constant は、通常だと module のレベルで定義されます。すべて大文字で書き、単語の区切りは underscore を使います。

例:MAX_OVERFLOW, TOTAL など

Designing for Inheritance(継承のための設計)

class の method や instance 変数の(総称:属性 attribute)を public or non-public にするべきかを、常に(あらかじめ)決めておくこと。public を後から non-public に変更するよりも、後から public に変更するほうが簡単です。

public attribute は、あなたの class を関係のない(外部の)client が使用することを期待しています。なので、後方互換性のない変更を避ける(維持を約束する)ものです。

non-public attribute は、(第三者)third-party が利用することを意図しないことを示す attribute です。逆に non-public 属性のものは、変更や削除の保証をしていません。

ここでは "private" という用語は使いません。python は本当の意味での private attribute は存在していません。(実現するには、不必要なたくさんの処理が必要になるから)

もうひとつの attribute の category として "subclass API" があります。(他の言語では、よく "protected" と呼ばれます)

class の中には継承されることを前提に設計されているものがあります。この class の動作の拡張/変更のために subclass を使うことが想定している attribute です。

どれが public attribute にするのか、どれが subclass API で、どれが基底 class だけで使う non-public なのかを、明確に決定するように注意しましょう。

ここまでの内容を踏まえて、(python 的な)pythonic なガイドラインを示します:

  • public attribute には、頭に underscore をつけるべきではありません。
  • もしも、public attribute の名前が予約語と衝突するときは、属性名の末尾に underscore をひとつ追加します。これは、省略や間違ったスペル corrupted spelling よりも望ましいです。(しかしながら、この規則に関わらず、class であることがわかっている変数や引数、特に class method の最初の引数には cls が例外的に好まれる)
    • Note 1: class method については、前述のルールを参照すること。
  • 単純な public データの attribute では、複雑な accessor/mutator method を使わずに、attribute の名前だけを公開することがベストです。単純なデータの attribute が(後から)機能的な振る舞いを成長させる必要があることに気づいたとしても、python は将来的な機能拡張の簡単な方法を提供している、と心に留めておくこと。そのようなケースでは、property を使用して、単純なデータ attribute の access 構文のあとに機能的な実装を隠します。
    • Note 1: 機能的な振る舞いには、副作用 side-effect のないように保ちます。ただし、cache などは一般的に問題ありません。
    • Note 2: 計算量の多い処理には proprty を使うことを避けること。attribute の表現は、呼び出す側も access が(比較的)軽い処理だと信じています。
  • もしも、class は subclass 化されることを想定していたとします。subclass には使わせたくない attribute があるときは、頭に underscore を2つ付けた名前を検討してください。(ただし、末尾にも underscore を2つ付けると __a__ になるから駄目で __a にする)この名前は name mangling の algorithm の機能を呼び出します。subclass が同じ名前の attribute を持つときは、attribute の衝突を回避できます。
    • Note 1: name mangling は、単純に class の名前が(attribute の名前に対して追加で)埋め込まれるだけなので、subclass がまったく同じ class の名前と attribute の名前を持つと衝突します。
    • Note 2: name mangling は debug や __getattr__() を使いたいようなときに、不便になっている恐れがある。しかしながら、name mangling の algorithm はよく document 化されているので manual で実行することも簡単です。
    • Note 3: name mangling を誰もが好むわけではありません。名前の衝突を回避する(本当のところの)必要性と、高度な使い方になることとのバランスを考慮すること。

Public and Internal Interfaces(公開型 interface と内部型 interface)

後方互換性の保証(の必要性)は、public interface にのみ適用されます。したがって、ユーザーは public interface と internal interface を明確に区別できるようにすることは大切です。

文書化された interface は、文書で明示的に「暫定的 provisional」or「内部型 internal」とされていない限り、(デフォルトで)public interface だと、みなされます。また、文書化されていない interface は、すべて internal interface だとみなされるべきです。

introspection をより良い形で support するために module は __all__ attribute を使用して、public API の名前を明示的に宣言する必要があります。

introspection とは、実行中の object の情報をプログラムの中から調べること。C# だと Reflection が近い。たとえば type() は object の型を取得し、dir() は利用可能な attribute, method を一覧にします。

__all__ = [] を空っぽのリストに設定することは、その module には public API が存在しないことを示します。

__all__ を適切に設定していても、internal interface(package, module, class, function, attribute, その他の名前)には、先頭に underscore の prefiex をひとつ付けて名前をつけるべきです。

もしも、ある名前空間に存在する package, module, class が internal なものであるなら、その中に存在するinterface もまた internal であるとみなされます。

import した名前は、常に実装の詳細である、とみなされるべきです。他の module は os.path__init__ のように文書化されている場合を除いて、それらの名前を間接的に参照するべきではありません。

他の module から import した名前 (function, class) などは、基本的にその module の内部実装です。なので public API 以外は使うな、依存するなという話をしているだけだと思う。

Programming Recommendations(プログラミングの推奨事項)

  • code は、他の python の実装 (PyPy, Jython, IronPython, Cython, Psyco, ...) の不利益にならない書き方をするべきです。
    • 例えば a += ba = a + b のような形式で書かれる statement、(in-place) string の連結において、CPython の効率的な実装に頼ってはいけないです。この最適化は CPython でも不安定(いくつかの型にしか効果がない)で、参照カウント refcounting を使わない他の実装では、(最適化は)まったく存在していません。ライブラリの performance に敏感な部分では、代わりに ''.join() を使うべきです。これによって、さまざまな実装において、連結処理が線形時間で行われるようになります。
# Correct
def good_concat(words):
    return ''.join(words)

print(good_concat(["a"] * 10000))
# Wrong
def bad_concat(words):
    result = ""
    for word in words:
        result += word  # 実装依存
    return result

print(bad_concat(["a"] * 10000))

最初に文字列の長さを計算して buffer を1回で確保してから一括で連結するようなこと。

  • None のような singleton との比較は isis not を使うこと。==!= のような等値演算子 equality operator は使わないこと。
    • また、もしも if x is not None のつもりのとき if x と書いているなら注意してください。例えば、default の値が None に設定されている変数や引数に対して、なにか別の値が設定されているかどうかをテストするケースがあります。ここの他の値は boolean 型の文脈 context では false と評価される型(例えば container など)かもしれません。
  • not ... is よりも is not 演算子を使うこと。どちらの表現も機能的には同じですが is not のほうが読みやすい。
# Correct:
if foo is not None:
# Wrong:
if not foo is None:
  • 拡張比較 rich comparison による order 操作を実装するとき、他 code が特定の比較しか使わないと期待するのではなくて、6つすべての演算子を実装することが best です。
    • __eq__, __ne__, __lt__, __le__, __gt__, __ge__
  • (実装に)必要な手間を最小にするために functools.total_ordering() decorator は、附則している比較 method を(自動で)生成する tool を提供します。
  • PEP 207 によると python では(比較において)反射性 reflexivity のルールを前提としています。よって、interpretery > xx < y に入れ替えたり Y >= xx <= y を入れ替えたり X == yx != y の引数を入れ替えたりすることがあります。
    • sort()min() 演算子< 演算子を使って max() 演算子> を使って動作することが保証されています。
  • しかし、他の文脈 context において混乱が生じないように(繰り返しになっているけど)6つの演算子をすべて実装しておくことが best です。
# Wrong
class Item:
    def __lt__(self, other):  # < 定義
        return self.value < other.value

# max() を使うと __gt__ が呼ばれるので ERROR
max([Item(), Item()])
  • lambda 式を識別子に直接 bind する代入文 assignment statement の代わりに、常に def statement を使うこと。
    • 最初の形式 def は、生成される function object の名前が明示的に f になりますが lambda を使うと、名前は lambda という汎用的な名前になることを意味します。
    • このやり方 def のほうが traceback(エラー表示)や、文字列表現の全般において有利です。代入文 assignment statement を使うと lambda 式が提供できる唯一の利点(おおきな式に対して埋め込めること)を失ってしまいます。
# Correct:
def f(x): return 2*x
# Wrong:
f = lambda x: 2*x
  • exception chaining を適切に使うこと。raise X from Y は、もとの traceback を失うことなく、明示的に(例外を)置き換える意図を示すために使用するべきです。
    • inner exception を意図的に置き換えるとき(raise X from None を使う)、新しい exception に関連する詳細を伝えるようにします。
    • (KeyError を AttributeError に変換するとき、attribute name を保持したり、もとの exception の text を埋め込む、など)
try:
    int("abc")
except ValueError as e:
    # RuntimeError への置き換えをするが e を引き継いで伝えている
    raise RuntimeError("入力の変換に失敗しました") from e
  • exception を catch するときは、(指定のない)except: を使う代わりに、可能な限り特定された exception を指定すること:
    • (指定のない)except は、 SystemExit と KeyboardInterrupt のような例外も catch してしまいます。なので Ctrl+C によるプログラムの中断を難しくしてしまいます。もしも、program の error を示したすべての exception を catch したいなら except Exception: を使います。ちなみに(指定のない)except は except BaseException: と同義です。
    • 経験則として、(指定のない)except の使用は2つのケースに限定するとよいと思います。
      1. traceback(エラー内容)を出力 or ログに記録するとき。すくなくとも、ユーザーはerror が発生したことを認識できます。
      2. もしも code がなんらかの cleanup の作業をする必要があるが、raise でexception を(上方 upward に)伝播させてしまうとき。このケースは try...finally のほうが良い方法になるかも。
try:
    import platform_specific_module
except ImportError:
    platform_specific_module = None
  • operationg system の error を catch するときは errno の値から判定 introspection するよりも、python 3.3 で導入された明示的な exception 階層を使うと良いです。
import errno

try:
    open("no_such_file.txt")
except OSError as e:
    if e.errno == errno.ENOENT:
        print("ファイルが見つからない")
try:
    open("no_such_file.txt")
except FileNotFoundError:
    print("ファイルが見つからない")
  • すべての try/except 節において try 節は、最低限の code に制限してください。これは bug の隠蔽を避けるためです:
# Correct:
try:
    value = collection[key]
except KeyError:
    return key_not_found(key)
else:
    return handle_value(value)
try:
    # 範囲が広すぎる
    return handle_value(collection[key])
except KeyError:
    # handle_value() が発生させた KeyError も catch してしまう
    return key_not_found(key)rrno values.
  • resource が code の特定の section に局所的に存在するときは with statement を使用して、使用後に確実かつ速やかに cleanup されるようにします。
    • try/finally statement も同様のことができます。
with open("data.txt", "r") as f:
    data = f.read()
# ここで自動的に f.close() が呼ばれる
f = open("data.txt", "r")
try:
    data = f.read()
finally:
    f.close()
  • context manager は、resource の獲得 or 解放以外のことをするなら、別の function or method を通して呼び出されるべきです。
    • 後者の例 with conn: では __enter__ method と __exit__ method が transaction の接続を閉じる以外に、なにをするのか情報が何もない。(なにをするのか)明示的に示すことが重要です。
# Correct:
with conn.begin_transaction():
    do_stuff_in_transaction(conn)
# Wrong:
with conn:
    do_stuff_in_transaction(conn)

with は明確な resource 操作をしたい。なので with の意図を明確にしておく。なので function/method を通すと function/method の名前で意図を示すことができる。

  • return statement には、一貫性を持たせること。fuction の return statement は(値や式を)すべて返却するか、すべて何も返却しないか、のどちらかにしなければいけません。
    • もしも return statement が、式 expression を返却するなら、値を返さない return のところは return None を書きます。明示的な return statement は function の最後に提供されるべきです。(その位置に到達できるなら)
# Correct:
def foo(x):
    if x >= 0:
        return math.sqrt(x)
    else:
        return None

def bar(x):
    if x < 0:
        return None
    return math.sqrt(x)
# Wrong:
def foo(x):
    if x >= 0:
        return math.sqrt(x)
    # return がない

def bar(x):
    if x < 0:
        return # 値がない
    return math.sqrt(x)
  • prefix や suffix を確認するときは文字列の slice の代わりに ''.startswith()''.endswith() を使うこと。コードがすっきりしていて error も起こりにくいです:
# Correct:
if foo.startswith('bar'):
# Wrong:
if foo[:3] == 'bar':
  • object の型比較は、型を直接比較するのではなくて、常に isinstance() を使うべきである:
# Correct:
if isinstance(obj, int):
# Wrong:
if type(obj) is type(1):
  • sequence (string, list, tuple) については、空っぽの sequence は false であるという(python の)特性を利用すること:
# Correct:
if not seq:
if seq:
# Wrong:
if len(seq):
if not len(seq):

空の list かどうかを調べるために len(seq) == 0 を書かないってことだと思う。

  • 末尾の空白 whitespace に依存した文字列 literal は書かないこと。このような(末尾の)空白は視覚的に判断できないし、editor によっては(最近だと eindent.py) trim してしまう。
# Wrong:
text = "ユーザー名: "  # 末尾に空白がひとつある(でも見えない)
input(text)
  • bool 値を true or false と比較するために == を使わないこと:
# Correct:
if greeting:
if not greeting:
# Wrong:
if greeting == True:

# Wrong(Worse):
if greeting is True:
  • try...finallyfinally suite の中で return/break/continue といったフロー制御文 flow control statement を使用することは推奨されません。なぜなら、このような statement は finally suite を伝搬している active な exception を暗黙のうちに cancel してしまうからです:
# Wrong:
def foo():
    try:
        1 / 0  # ZeroDivisionError が発生
    finally:
        return 42  # return により exception を無視

Function Annotations

PEP 484 が採用されたことで、function annotation の style rule は変更されました。

  • function annotation は PEP 484 の構文を使うべきです。(前の section に annotation については、いくつかの推奨があります)
  • PEP 8(の過去のもの)で以前に推奨していた annotation style(の実験版)はもう推奨されていません。
  • しかしながら、標準ライブラリ以外では、PEP 484 のルールの範囲内での実験的使用は推奨されています。例えば、大規模な third-party のライブラリや application を PEP 484 style の型 annotation で markup して、それらの annotation を追加する難しさを review して、それらの存在によって code の理解のしやすさを向上するのかどうかを観察するといったことです。
  • python の標準ライブラリはこのような annotation の採用には慎重(保守的)であるべきですが、新しい code や大きな refactoring では、その使用を認めています。
  • function annotation を別の形で使いたい code には、以下のような comment を付けることを推奨します:
    • # type: ignore # 型検査を無効化する
    • (ファイルの先頭近くに書くことで)これは type checker がすべての annotation を無視するように指示します。(より細かい制御の方法は PEP 484 を参照すること)
  • linter と同様に type checker は、optional な外部の tool です。pythoninterpreter は type check による message を発行させないこと。annotation に基づいて振る舞いを変更するべきではありません。
  • type checker を使いたくないユーザーは、自由にそれを無視できます。しかしながら、third-party のライブラリを使うユーザーは、package で type checker を実行したいと思うかもしれません。この目的のために PEP 484 は stub file .pyi の使用を推奨しています。.pyi file は対応する .py file の代わりに type checker によって読み込まれます。stub file はライブラリと一緒に配布することもできるし(ライブラリ作者の許可のもと)typeshed repo を通じて個別に配布することもできます。

Variable Annotations

PEP 526 では variable annotation が追加されました。variable annotation の推奨 style は、前述の function annotation と同じです:

  • module レベルの変数、class 変数、instance 変数、local 変数の annotation は colon : の後に半角 space をひとつ入れるべきです。
  • colon の前後に space を入れるべきではありません。
  • もしも、代入に右辺があるなら、等号の両辺にはひとつずつの space を入れるべきです。
# Correct:
code: int

class Point:
    coords: Tuple[int, int]
    label: str = '<unknown>'
# Wrong:

code:int  # colon の後に space がない
code : int  # colon の前に space がある

class Test:
    result: int=0  # '=' の前後に space がない

PEP 526 は python 3.6 から採用されていますが、この variable annotation は、すべての python 向けの stub file .pyi でも推奨される構文です。(詳しくは PEP 484 を参照)

参考

Python - PEP20 The Zen of Python

PEP20 について学習しました。

The Zen of Python は、下記のように pythonプログラマーに向けた心構え・イディオムをまとめたようなもの。Zen はそのまま "禅" みたい。

ただ著者がこのタイトルを直接つけたという感じではなさそうで Strunk & White "Elements of Style" みたいな姿勢くらい?

The Zen Of Python: A Most In Depth Article

この PEP を執筆したのは「Tim Peters」さん。古参の優れた python 貢献者でした。近年は(トラブルの多い)python の理事会から BAN を受けたりもしていて、python コミュニティの複雑な一面を感じたりもしました。

Abstract(概要)

Long time Pythoneer Tim Peters succinctly channels the BDFL’s guiding principles for Python’s design into 20 aphorisms, only 19 of which have been written down.

長年の Pythoneer である Tim Peters は、Python の設計に関する BDFL の指針を 20 の格言に簡潔にまとめていますが、そのうち 19 のみが文書化されています。(のこりの1つは、ジョーク。最初から存在していない)

NOTE: Mysteriously, only 19 of the guidelines are written down. Guido van Rosum reportedly said that the missing 20th aphorism is “some bizarre Tim Peters in-joke.” - The Invent with Python Blog

BDFL = 「Benevolent Dictator For Life」のこと。つまりは開発のリーダーのこと。

本文

1. Beautiful is better than ugly.

美しいほうが、醜いよりも良い。

意味はそのまま。コードは美しいほうがいい。それはそう。

もう少し補足すると、すべてのスクリプトコードが美しい必要はないし、美しさは主観的なものだ。

とはいえ、Python はコードを美しくする作業もそれほど複雑なことではないはず。

2. Explicit is better than implicit.

暗示よりも明示のほうが良い。

例えば、どのモジュールから、どういったものが提供されているのか、明示(わかるように)したほうがいい、ということだと思う。

NG コード:

from os import *

OK コード:

import os
print(os.getcwd())

引数の意味や、処理を明らかにすることで、コードの意図を明示的にしましょう、ということを推奨しています。

wildcard のインポートのようなものは、便利かもしれませんが、namespace が汚染されるので、個人的にも嫌いな書き方です。

3. Simple is better than complex.

シンプルなものは、複雑なものよりも良い。

コードのリファクタリングについての教本のほとんどで書いてあることだと思います。割愛。

4. Complex is better than complicated.

複雑なものは、complicated よりも良い。

ここでは、complicated をどのように訳し解釈するのかがポイントですね。調べると、日本では complicated を「込み入った」と解釈している翻訳が多いように思いました。

「込み入った」とは、色々な解釈があると思うけど、私の理解は、処理が整理されずに詰め込まれていて、コードのリファクタリングが必要な状態を指すと思います。コードの詳細を知らないと動作を予想できない状態。雑で怠惰なコード。

「複雑なもの」とは、単純ではないものの、コードのリファクタリングが必要な状態ではない。コードの詳細を知らなくても動作を予想できる状態。(構造と論理が難しいので単純化できないだけの)賢いコード。

結論だけを見ると、それはそうだろうという指摘だと思う。

5. Flat is better than nested.

フラット(平坦)なものは、ネスト(階層)のものよりも良い。

ここでの指摘は何を指すのか、微妙なところです。次は、名前空間は浅く整理された内容が良いことについて指摘されています。これは、python の特徴のように思います。(C# とはすこし用途・文化が違う)

PEP 423 に「Avoid deep nesting」という内容があります。ここでは、2階層までという紹介です。

Yes: “pyranha”
Yes: “pythonsport.climbing”
Yes: “pythonsport.forestmap”
No: “pythonsport.maps.forest”

一方で、次はコード自体のネストについてコメントされていて、ガード節の "early returns" について言及されています。

def process(x):
    # ガード節
    if not valid(x):
        return
    # ここから本命の処理:ネストせずフラットに記述
    do_something(x)

いずれにしても、ネストを嫌うのはここまでの1~4の理由があるように思うこと。プログラム実行の形式の都合から、コードは単純であることの価値が C# と比べても高いように考えています。

6. Sparse is better than dense.

まばら(隙間)は、密集しているよりも良い。

可読性に関する指摘です。

NG コード:

print('\n'.join("%i bytes = %i bits which has %i possible values." %
                (j, j*8, 256**j-1) for j in (1 << i for i in range(8))))

OK コード:

for i in range(8):
    j = 1 << i
    bits = j * 8
    values = 256**j - 1
    print(f"{j} bytes = {bits} bits which has {values} possible values.")

各行がなにをしているのかわかるようにしたほうが良い。 つまり、コードをメンテナンスしやすい形で記録しておくほうが良い、という話。

「dense」は効率的なシーンもあるけど「理解の疲労」が大きい。オープンソースのようになると、この「疲労」の存在は嫌だろう。

個人的に「dense」なコードを美しいコードだと解釈する人が一定数いると思う。つまり、それは。

7. Readability counts.

可読性こそ大切である。

ここでの count は「大切である、重きをなす」という意味。可読性については、リーダブルコードなどたくさんの参考があるので、割愛。

count を "善である"、と解釈しているものもあった。python の設計に関する指針なので、C 言語と python のコードを比較して、後者のほうが可読性が高くて "善" だ、といってもいいのかもしれないけど、コードを書くうえでの指針だと解釈したほうが得られるものがあると思う。

8. Special cases aren't special enough to break the rules.

特別扱いは、ルールを破るほどの特別な理由にならない。

常にスタイルや可読性、整合性を重視し、「特定のケースだから例外的に・・・」とルールを無視しないようにしましょう、という指針。

def process(values):
    result = []
    for v in values:
        if v == "":  # 特別ケース
            continue  # 空文字だけ例外扱い
        try:
            result.append(int(v))
        except ValueError:
            pass  # 他の文字のエラーはパス
    return result

"" の処理をしたいときは、process の中に含めないはず。

def process(values):
    result = []
    for v in values:
        try:
            num = int(v)
        except ValueError:
            continue
        result.append(num)
    return result

values = ["123", "", "456", ""]
filtered = [v for v in values if v != ""]  # 空文字を除く

# ["123", "456"]
process(filtered)

どうしても process の中で処理したいなら、それは例外的な扱いになるので次の「9」を参考にすることになる。

9. Although practicality beats purity.

とはいえ、実用性は純粋さに勝る。

8と矛盾する内容になっています。、主なルールをそのまま守りつつ、その上で明確に例外を設けるのが望ましいです。

つまり、「8」の例でいえば:

def process(values):
    result = []
    for v in values:
        if v == "":
            # 明示的にログを残す
            print("Skipping empty string", file=sys.stderr)
            continue
        try:
            num = int(v)
        except ValueError:
            print(f"Warning: cannot convert {v!r}", file=sys.stderr)
            continue
        result.append(num)
    return result

「8」と「9」はもちろんセットで理解し、言及する必要があります。

一般的にも、一貫性のないコードは困る。だけど、Java を例に挙げて説明すると、どんな小さなコードでもオブジェクト指向パラダイムに当てはめて設計をすると、多くのボイラープレートコードが生まれてしまう。

2つの境界をどのように歩くのかは「経験」という回答になっている。

Walking the line between these two aphorisms becomes easier with experience.

これは、説明になっているのか疑問だ。

10. Errors should never pass silently.

エラーは黙って見逃すべきではない。

NG コード:

def read_file(path):
    try:
        with open(path) as f:
            return f.read()
    except:
        pass  # エラーを無視
    return None

すべての例外を pass してしまうようなこと。問題が発生していても、見逃すことになるので、あとで原因を調査する手がかりがなくなる。(Silent Failures)

OK コード:

def read_file(path):
    try:
        with open(path) as f:
            return f.read()
    except FileNotFoundError:
        logging.error(f"File not found: {path}")
    except PermissionError:
        logging.error(f"No permission to read file: {path}")
    except Exception as e:
        logging.error(f"Unexpected error reading {path}: {e}")
    return None

主に発生する恐れのある例外は個別に処理、その他の例外についてもログを記録することで追跡可能にする。(ほとんど「11」の内容になる) 。

11. Unless explicitly silenced.

ただし、「あえて」エラーを無視すると明示されている場合は別とする。

これも、「10」とペアになっている。デフォルトでは、エラーを見逃さずに処理するべきなのは当たり前だけど、エラーを無視する場合は、それをコード上で、はっきりとその意図を示して書けばいい。

例えば suppress を使うことで、特定の例外を「この行では無視する」と明確に示すことができる。仕様の範囲が明確だ。

from contextlib import suppress
import os

# 明示的に FileNotFoundError を無視するケース
with suppress(FileNotFoundError):
    os.remove('temp.txt')  # ファイルがなければ無視

print("Continue execution")
def read_optional_config(path):
    try:
        with open(path) as f:
            return f.read()
    except FileNotFoundError:
        logging.debug(f"No config file found at {path}, using defaults")
        return None

これは FileNotFoundError だけ catch しています。それ以外の例外は処理されていないので pass されません。その他の例外は、普通にプログラムをクラッシュさせる。

つまり、正常な動作、仕様内の例外、それ以外は「想定外の致命的なエラー」としてはっきりさせて、運用するということだと思う。

12. In the face of ambiguity, refuse the temptation to guess.

曖昧な状態 (ambiguous) に直面したら、推測する誘惑を拒むこと。

言い直すと、直感や憶測で処理を進めてはいけない、だと思う。曖昧だとエラーになる=曖昧さがなければエラーは起きにくい。

NG ケース:

def get_positive(value):
    if value:
        return abs(value)
    # 0 や None、空文字でもここまで来てしまう
    return None  # ここは曖昧:何が返されるべきか判断できていない

OK ケース:

def get_positive(value):
    if value is None:
        raise ValueError("value must not be None")
    if not isinstance(value, (int, float)):
        raise TypeError(f"expected int or float, got {type(value).__name__}")
    if value <= 0:
        raise ValueError("value must be positive")
    return value

None など各ケースを明示的に検査して、曖昧な状況を拒否している。「仕様どおりに動く」コードを実現している。

ここで言いたいことは、例のケースのことを具体的に修正指示したいのではなくて、コードの意図が一件してわかるように全体を設計しましょう、ということ。

if not a and b よりも if (not a) and b が意図を明確化するなら後者のほうが良いということでもある。曖昧はよくない。意図を明確化しろ。

13. There should be one-- and preferably only one --obvious way to do it.

あるべきは「ひとつ」--できれば唯一の明白なやり方だ。

例えば、リストの長さを得るやり方は、通常こうする:

length = len(my_list)

やろうと思えば for や index のループを繰り返して要素数を数えるメソッドを作ることもできる。しかし、そうするべきではない。できるだけわかりやすい「ひとつ」の書き方が python には備わっているべきだ、だという考え。

転じると、python ユーザーは、もっともわかりやすい書き方を利用することでコードは統一されるし、読みやすくなるよ、ということ。

dict をマージするときはどうするのか? 以下はよくない:

d = dict(x=1, y=2)
d2 = dict(y=3, z=4)
merged = d.copy()
merged.update(d2)

現在は以下の書き方がサポートされている:

merged = d | d2

なので、python の簡単な書き方のテクニックを習熟しよう。次の「14」で示すとおり。

14. Although that way may not be obvious at first unless you're Dutch.

ただし、オランダ人 (Guido van Rossum) のような人でない限り、最初そのやり方は、明白でないかもしれない。

これは「13」とペアでセットになっています。

python には簡単な書き方が用意されてるけど、それが "明白なもの" だと理解するまでには、学習・経験が必要だよね、ということ。

ここで、オランダ人は最初からわかるように書いてあるのは、このオランダ人は (Guido van Rossum) を指しており、(Python の生みの親だからわかるという)ジョーク。

15. Now is better than never.

やらないよりは、今やるべき。

ここまでに色々な格言(指針)があったけど、コーダーが完璧な状態になるのを待って手をいつまでも付けないよりは、まずは作ってみるべきだ、という考え。

良い例だな、と思ったのは、コードを初期の段階から最適化することが難しいケースがあって、ソートの実装が必要だったとしても、最初は単純なもので仮に実装していても良いと思う。

テスト段階はバブルソートで、後から効率のよいソートに置き換えるという方針は「16」にも該当しないし悪くない。

16. Although never is often better than right now.

しかし「今」するより、やらない方がましなこともある。

これも「15」とセットなのは明白です。実装のために急いで作るより、やらない方がマシな場合もある、という注意喚起がついています。

焦りすぎて誤った方向に進まないように慎重さも求めています。

単に動くだけのものを作るのではなくて、型チェックや例外処理を加えて信頼性を向上させるとか、品質を上げる方向に改善していく。

"fail fast" ではなく "build fast" と "think fast" を両立させる姿勢のこと。

17. If the implementation is hard to explain, it's a bad idea.

もしも、実装の内容を説明することが難しいのなら、それは悪いアイデア(の実装)です。

これは、具体的な指針なので、それほど詳しい説明はいらないと思う。

コーダーにとって理解しづらい、説明しづらいコードはトリッキーなので避けようという考えだと思います。レビューをする上でも「7」の可読性が低いコードは「17」にも該当してしまう。

なにをしているのか説明に手間のかかる(かもしれない)コード:

def flatten(lst):
    return sum((x if isinstance(x, list) else [x] for x in lst), [])

説明のしやすい「実装」コード:

def flatten(lst):
    result = []
    for x in lst:
        if isinstance(x, list):
            result.extend(x)
        else:
            result.append(x)
    return result

ここまでの内容からもわかるとおり、コードの意図が明白であること、コードが読みやすいこと、コードがメンテナンスしやすいことが大切。

あたりまえだけど、本当にパフォーマンスが必要なら C や C++ を採用したほうが最終的な実行速度を追求できるはずだ。その点で、python はタイムパフォーマンスがいいと思う。開発やデバッグにかかる時間の面で、優れるケースが多いと思う。言語としての強みを意識すると「可読性」、「メンテナンス性」、「明白さ」といったところを整理したコードの価値が高いという考えになるのだと(私は)考えています。

18. If the implementation is easy to explain, it may be a good idea.

もしも、実装を説明しやすければ、それは良いアイデアかも。

これは「人が読んで納得できる実装であること」の重要性の目安になると思います。ただ、「たぶん」くらいの感じなので、必ずしも正解としておらず判断基準として有益だろうという立ち位置だと思います。

これも言語の文化的に、まずは、理解しやすいコーディングを選んだほうがいいんじゃないかな、くらいの姿勢かと。

「17」の指摘と同じように見えるのでそれだけで十分なようにも思ったのですが、丁寧に考えるなら、「17」は止めた方がよいことを断言する形で否定し「18」は断言しない形でゆるい。

具体的な例にすると、「説明しづらい」には該当せず、「説明しやすい」にも該当しない≒「説明は可能なライン」のコードなどがあると思う。これを全部除外してはいけないよね、ということかも。

19. Namespaces are one honking great idea -- let's do more of those!

名前空間(namespace)はすごく素晴らしいアイデアだ。もっと活用しよう!

名前空間を使えってこと。名前の衝突を避けるために、コードを(シンプルな名前空間を使って)構造的にコードを書け。

「2」でも説明したけど mymodule.func() のような書き方なら、名前でどこで定義されたものかが一目でわかる。

特に規模が大きくなるほど、明示的な名前空間の設計でコードを組織化して、保守が楽になる。

ただし、名前空間の深さは2つまでを python は推奨してる。PEP 423

参考

WPF IValueConverter の小ネタとテクニックの整理

WPF の IValueConverter を実装するときの考え方と小ネタ。

MarkupExtension の実装と DependancyProperty の実装

IValueConverterIMultiValueConverter でも ValueConverter はコンバーターにプロパティを実装したいときに MarkupExtension を使った実装も、DependancyProperty を使った実装も(一応)どちらでも可能です。

個人的なポイントとしては、MarkupExtension を使った実装をすると、IValueConverter のパラメータを追加する手法としてはシンプルで自然なものになります。この特徴として、XAML のスコープごとに異なる設定を持った Converter を使いやすい。柔軟なパラメータの代入が可能です。

<CheckBox IsEnabled="{Binding Sample, Converter={local:SampleConverter SampleParam=1}}" />
<CheckBox IsEnabled="{Binding Sample, Converter={local:SampleConverter SampleParam=3}}" />

ValueConverter を使った実装は、いくらか個性的な(または、トリッキーな)手法になる恐れがあり、あまり優位性はないと思いますが、ネット上にはこの設計をしているものもあります。

メリットは、引数の型を決めることができます。string 型ではなくて class や enum の値を初期設定しやすいと思います。

基本的に ValueConverter は、Binding しない。Binding したいときは、該当するパラメータを MultiBindingConverter にして引数として渡すほうがよい。

<local:SampleConverter Key="SampleConverter1" SampleParam="A" />
<!--(要:Freezable) -->
<local:SampleConverter Key="SampleConverter1" SampleParam="{Binding A}" />

パラメータがないときの実装パターン

このときは ConverterParameter で固定値の(基本的には文字列)のパラメータを代入します。

<Label Content="{Binding Text1, Converter={StaticResource NoParamConverter}, ConverterParameter=Param1}" />

MarkupExtension の実装パターン

public class SampleValueConverter : MarkupExtension, IValueConverter
{
    public string SampleParam { get; set; } = "";

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        Debug.WriteLine($"SampleParam = {SampleParam}");
        return this; // IValueConverter 自体を返す
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return SampleParam == "1";
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        => throw new NotImplementedException();
}

ProvideValue の設定が IValueConverter の場合は、ちょっと違和感があると思います。他の例だとこんなのはわかりやすい。

ProvideValue を実装する意味は、コンバーター自身を拡張プロパティの書き方ができるように認めてほしいから。

<CheckBox IsEnabled="{Binding Sample, Converter={local:SampleConverter SampleParam=1}}" />

Resources に宣言するのではなくて、上記の書き方のように突然 Converter の中で SampleConverter を設定したいときは MarkupExtension が必要になる。

Resources に対して StaticResource として登録したデータに SampleParam=1 という引数の部分を書けるようにするわけではないので注意。(個人的に混同したことがある)

Freezable を使う

以下のようにして、Binding をすることもできる。(ただし、Freezable は動作がトリッキーなので注意したい)

Param の変化には対応できないので、Param のインスタンスが変わらなければ、Param の同じインスタンス内のパラメーターを追い続けることができる(はず)。

<local:ParamEx3Converter x:Key="ParamEx3Converter1" Param="{Binding Param}" />
public class ParamEx3Converter : Freezable, IValueConverter
{
    public static readonly DependencyProperty ParamProperty =
        DependencyProperty.Register(nameof(Param), typeof(string), typeof(ParamEx3Converter), new PropertyMetadata(""));

    public string Param
    {
        get => (string)GetValue(ParamProperty);
        set => SetValue(ParamProperty, value);
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return $"{value} : {Param}";
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        => throw new NotImplementedException();

    protected override Freezable CreateInstanceCore()
        => new ParamEx3Converter();
}

Sample

GitHub に Converter の挙動を整理したサンプルを公開しています。

  • パラメーターなしのとき
  • MarkupExtension
  • DependencyObject
  • Freezable

最後にもう一度再掲載:

基本的に ValueConverter は、Binding しない。Binding したいときは、該当する Binding パラメータを MultiBindingConverter にして引数として渡すほうがよい。

参考

Python 空白の引数を有効にする

空白の引数は、VS Code などでもシェルレベルで取り除かれてしまうことがあります。

python .\実行ファイル.py ""
import sys

def test():
    print("len = " + len(sys.argv)) # 1

# テスト実行
test()

argv[0] は "実行ファイル.py" の部分が入るので "" が省略されています。

対策

stop-parsing token --% を追加することで空白の引数を受け取ることができます。

python --% .\実行ファイル.py ""

補足として "None" を引数から渡すことができないはずです。この場合はコード中で変換するとよいと思います。

if arg == "None":
    arg = None

参考

メンタルヘルスマネジメント検定試験(受験と合格)

2025 年 3 月 16 日 (日) にメンタルヘルスマネジメント試験(Ⅲ種)を受けてきました。同試験の中では一番難易度が易いものになります。受験した理由としては、第一種衛生管理者の資格を取得しているので類する付加価値の資格として受験しています。

結果は「合格」 82/100点

よかったです。

受験に際しての勉強は、以下の通りだったとメモ。参考書を読むのに(通勤時間などを利用して)ダラダラと読みつつ、試験の2週間前ごろから、過去問と YouTube を使った勉強を開始しました。

正直モチベーションが上がらずに雑になっていたので得点はイマイチな感じですが、納得性あります。80点はあるので、ある程度理解はできたかと。

メンタルヘルスマネジメント試験とは

ストレスを抱えたときの対処方法、心の不調による休職や離職等の率であったりストレスの主要な原因、ストレス反応の種類、ストレスの対応の考え方についてを学習するものです。

第一種衛生管理者とは違って、「主催:大阪商工会議所、施行商工会議所」なので、そこまで大きな資格ではありません。

年2回の試験でそれぞれ2万人くらいが受験しているので、そこそこ民間資格の中では形になっていると思います。日本郵便など、企業で取得を推奨するケースもあるようです。

受験した理由

単純に健康管理のためにメンタルヘルスマネジメントを学習するメリットがあると思いました。個人的な感想として、メンタルヘルスは不要なときには軽視されすぎで、必要になったときになって困ってると思います。心身のダメージも思ったよりも大きくなることがあって、人間関係や人生設計によくない影響を与えてしまうと辛い。

私の経験値としては、自分ではなくても、身近な人や会社の同僚などが様々な理由からメンタルヘルス不調になってしまうケースは、近年だと珍しくないと思っています。こうしたときに、どういった対応であったり、予後を予想したりできるのか、といったところでは考え方を知ることができた。

実務的にも、理解が必要だったという面もあった。メンタルヘルス不調の人への対応面で選択肢を増やすことができたし、メンタルヘルス不調からトラブルになったときにも(理解を深めたことで)変にモヤモヤしても、多少はストレス軽減につながる考え方ができるなと思った。

勉強に使ったもの

3つ。申請したのが2月6日。試験日が3月16日なので、1か月の勝負だったかなぁと思うので、それほど多くない。

参考書は公式のものですが、なんでもよい気はします。教科書で気を付ける点は、毎年更新されるデータ関係です。「1番ストレスに感じるもの」みたいなアンケート結果のようなものは、根拠となるデータの資料が更新されると数字が変わったりしているかも。

章ごとに必ず出題があるはずなので、ある程度は新しい参考書が望ましいとは思います。ただ、難易度的に新題を捨てても十分いけるような気もします。

YouTube は今回も結構役にたったと思います。電車の中や、ランニング中、ジムの運動中に聞いていたので、3回はリピートして聞いていたと思います。教科書を1回読んだあとは丁寧に読み直すこともなかった(動画で気になったところをあとで見直すだけ)ので、覚えがない問題は YouTube で聞いたことを思い出して回答しました。

学習期間

参考書の読書:2025/02/06 ~ 2025/03/01
勉強:2025/02/24 ~ 2025/03/15
勉強の内訳:

  • 参考書(10%)
  • 過去問(30%)
  • YouTube(60%)

試験のためだけに集中して時間を使ったのは10H~20Hくらい。

それほど確実に取得したい試験ではなかったので、日常のペースを変えずに、空いた時間に勉強したり、ジムの運動など習慣になっている時間の間を工夫して学習を進めた感じです。

費用

  • 受験料 5280 円
  • 参考書 1500 円
  • 交通費 - 円

合計:6,680

個人的には、会社の資格取得補助金を利用したので、自己負担は実質 2,000 円くらいだと思う。参考書は、第一種衛生管理者と同じく中古品を利用した。

免許の発行も必要ないので、受験したらおしまい。プチ試験。

参考

ファイル名を datetime に変更するバッチファイル

とりあえず、ファイル名を置き換えたいときに利用する。 バッチファイルが一瞬走るんでちょっとうざいけど、問題なく動作した。

@echo off
setlocal enabledelayedexpansion

:: 現在の日付と時間を取得(yyMMdd HH.mm.ss.ff)
for /f "tokens=2 delims==" %%I in ('wmic os get localdatetime /value') do set datetime=%%I
set YY=%datetime:~2,2%
set MM=%datetime:~4,2%
set DD=%datetime:~6,2%
set HH=%datetime:~8,2%
set MIN=%datetime:~10,2%
set SS=%datetime:~12,2%
set FF=%datetime:~15,2%

:: カウンター(ファイルごとに連番を振る)
set COUNT=0

:: 各ファイルの処理
for %%F in (%*) do (
    set /a COUNT+=1
    set "filename=%%~nxF"
    set "filepath=%%~dpF"
    set "outputs_folder=%%~dpFoutputs"

    :: outputs フォルダを作成
    if not exist "!outputs_folder!" mkdir "!outputs_folder!"

    :: 連番を3桁で設定
    set "num=00!COUNT!"
    set "num=!num:~-3!"

    :: 新しいファイル名を作成(拡張子を維持)
    set "newname=data_!YY!!MM!!DD! !HH!.!MIN!.!SS!.!FF!_!num!%%~xF"

    :: copy コマンドでリネーム&コピー(元ファイルは残す)
    copy /Y "%%~F" "!outputs_folder!\!newname!"

    echo Copied and renamed: %%~F → "!outputs_folder!\!newname!"
)

pause

出力は、ドラッグアンドドロップしたファイルのあるフォルダーに output フォルダーを作成して、その中に移動する。 複数ファイルをドラッグアンドドロップされたときのために、末尾に連番 "_00x" を付与している。

data_250310 18.06.53.73_001.ext

ファイルを残さず移動させたいときは move がいいと思う。

move "%%~F" "!outputs_folder!\!newname!" 

参考