💭

python3のクラス定義には、何でも(変数と関数 以外)書ける

2021/09/10に公開

python3でのclass定義は、他の言語と異なり、ランタイムの処理であり、実は何でも書ける。これは公式ドキュメントに明確に書かれている。

9. クラス — Python 3.9.4 ドキュメント

実際には、クラス定義の内側にある文は、通常は関数定義になりますが、他の文を書くこともでき、それが役に立つこともあります --- これについては後で述べます。クラス内の関数定義は通常、メソッドの呼び出し規約で決められた独特の形式の引数リストを持ちます --- これについても後で述べます。

assertしたりできる

具体的例を示そう。

inclass.py
class test(object):
    assert True
    def a(self): pass
    None
    1+3
    var1: int
    int(1)

print(test.__dict__)

このコードは何のエラーもなく終了して、testクラスには、メソッドaとvar1だけが残る。

だが他の式が無効になるわけではない。例えばassert Falseにすると、クラス定義がAssertionErrorで中断する。

$ python3 inclass.py 
Traceback (most recent call last):
  File "/home/honda/WORK/atk/inclass.py", line 9, in <module>
    class test(object,metaclass=metadump):
  File "/home/honda/WORK/atk/inclass.py", line 10, in test
    assert False
AssertionError

ifも書ける

次の例では、メソッドbだけが定義される。

class test(object):
    if False:
        def a(self): pass
    else:
        def b(self): pass

print(test.__dict__)

これはpythonのクラス定義が、極めて動的であることの証左だろう。
だが、pythonの処理としては珍しくはない。実はこれはimportの時のモジュールのロード処理とほとんど同じなのだ。

「クラスオブジェクトは、基本的にはクラス定義で作成された名前空間の内容をくるむラッパ (wrapper) です。」

この挙動については前出のリンクでも触れている。

9. クラス — Python 3.9.4 ドキュメント

クラス定義に入ると、新たな名前空間が作成され、ローカルな名前空間として使われます --- 従って、ローカルな変数に対する全ての代入はこの新たな名前空間に入ります。特に、関数定義を行うと、新たな関数の名前はこの名前空間に結び付けられます。

クラス定義から普通に (定義の終端に到達して) 抜けると、 クラスオブジェクト (class object) が生成されます。クラスオブジェクトは、基本的にはクラス定義で作成された名前空間の内容をくるむラッパ (wrapper) です。クラスオブジェクトについては次の節で詳しく学ぶことにします。 (クラス定義に入る前に有効だった) 元のローカルスコープが復帰し、生成されたクラスオブジェクトは復帰したローカルスコープにクラス定義のヘッダで指定した名前 (上の例では ClassName) で結び付けられます。

perl5

類似の処理は、perl5でも使われてる。
Perl のクラスって個性的ですね!(生で見えるオブジェクト指向) - Qiita

perl5の場合は、パッケージ(pythonのモジュールと同義)==クラス とすることで処理を大幅に簡素化している。

Discussion