Python 標準ライブラリ types の紹介

Python [types][link-types] ライブラリ。標準ライブラリでありながら名前から何をしてくれるライブラリなのかがわかりにくい一品です。そんな [types][link-types] を紹介します。

頭の片隅に置いておくと、ふとした時に役に立つ、そんないぶし銀なライブラリです。

[link-types]: https://docs.python.org/ja/3/library/types.html

## [組み込み型](https://docs.python.org/ja/3.10/library/types.html#standard-interpreter-types)へのアクセス
組み込みインスタンス None の型は？ 関数オブジェクトの型は？  types ライブラリはそういった「builtin 空間に置くほどではない、しかしそれでもインタプリタが使用していて存在している型」へのアクセスのための口であります。

```python
>>> types.NoneType is type(None)
True

>>> def a(): pass
>>> isinstance(a, types.FunctionType)
True
```

Python 2.7 のドキュメントを確認すると、この機能だけが記述されています。 [types][link-types] の当初の役目はこれだけだったのでしょう。しかし、その後、型関連のユーティリティ関数が追加されていくことになりました。

## [MappingProxyType][link-MappingProxyType] 読み出し専用のマッピングのプロキシ
組み込みのシーケンスには読み書きができる list と読み出し専用の tuple がありますが、組み込みのマッピングには読み書きができる dict だけがあり読み出し専用のものがありません。集合型 set にすら対になる読み出し専用集合 frozenset があるにもかかわらず、です。更新不可能なマッピングをつくりたい。そんな希望を間接的に解決してくれるのが [types.MappingProxyType][link-MappingProxyType] です。

[link-MappingProxyType]: https://docs.python.org/ja/3/library/types.html#types.MappingProxyType

[MappingProxyType][link-MappingProxyType] に dict などのマッピングを渡すと、読み出し専用のビューが得られます。元となったマッピングは依然として存在しており変更が加わるとビュー側にも影響が及ぶため真に不変というわけではないです。それでも元のマッピングを外部から隠しておくことで、誤操作で変更されるといった事態を防ぐことができます。

```python
proxy = types.MappingProxyType({0: 1})
print(proxy[0])  # 1
print(0 in proxy)  # True
proxy[0] = 100  # TypeError
```

## [SimpleNamespace][link-SimpleNamespace] 名前空間の作成
データ、たとえばとあるアプリケーションの設定や定数をおいておいて後で取り出すための名前空間が欲しい。でもモジュールグローバルは汚したくない。これのためだけのモジュールは過剰、空のクラスでも過剰。 object だと不足。 dict だとカッコと文字列が面倒。そんな要望に応えてくれるのが [types.SimpleNamespace][link-SimpleNamespace] です。

[link-SimpleNamespace]: https://docs.python.org/ja/3/library/types.html#types.SimpleNamespace

```python
>>> conf = types.SimpleNamespace()
>>> import datetime
>>> conf.date = datetime.date(2022, 12, 6)
>>> conf.title = 'Python 標準ライブラリ types の紹介'
>>> conf
namespace(date=datetime.date(2022, 12, 6), title='Python 標準ライブラリ types の紹介')
>>> print(f'{conf.title}: 公開日 {conf.date}')
Python 標準ライブラリ types の紹介: 公開日 2022-12-06
```

ドキュメントにもあるようになんらかのデータソースからの入力を表現するような、インスタンスを大量に作っては破棄してをくり返す予定のレコード型を用意したい場合は [collections.namedtuple](https://docs.python.org/ja/3.10/library/collections.html#collections.namedtuple), [typing.NamedTuple](https://docs.python.org/ja/3.10/library/typing.html#typing.NamedTuple) のほうが適しています。

## [new_class][link-new-class] 動的な型生成
class 文を使わずにクラスを作る方法として、 type をはじめとするメタクラスを実行するという方法があります。しかし、その作法はどうしても複雑になってしまっているものです。そこで、いつもの手順とでもいうべき処理をまとめてくれているのが [types.new_class][link-new-class] です。

[link-new-class]: https://docs.python.org/ja/3/library/types.html#types.SimpleNamespace

```python
>>> X = types.new_class('X')
>>> obj = X()
>>> obj
<types.X object at 0x0000015F145AA610>
```

types.new_class コード(Python 3.11.0): [https://github.com/python/cpython/blob/v3.11.0/Lib/types.py#L67](https://github.com/python/cpython/blob/v3.11.0/Lib/types.py#L67)

## [DynamicClassAttribute][link-DynamicClassAttribute] クラス属性のカスタマイズ
[property][link-builtins-property] と同じように使えるデコレータ、それが [types.DynamicClassAttribute][link-DynamicClassAttribute] です。 [property][link-builtins-property] との違いは、クラス越しにアクセスされたときに AttributeError を送出することです。このエラーで \_\_getattr\_\_ メソッドの呼び出しを促すことにより、インスタンス越しの属性アクセスとクラス越しでの属性アクセスの結果を出しわける実装が可能になります。 [enum.Enum](https://docs.python.org/ja/3.10/library/enum.html) の name, value の機構で使われているものです。

[link-DynamicClassAttribute]: https://docs.python.org/ja/3.10/library/types.html#types.DynamicClassAttribute
[link-builtins-property]: https://docs.python.org/ja/3.11/library/functions.html#property

```python
import types

class A:
    @types.DynamicClassAttribute
    def d(self):
        return 'dynamic_class_attr'

    @property
    def p(self):
        return 'property'

a = A()
print(a.d)  # 'dynamic_class_attr'
print(a.p)  # 'property'

# A.d  # AttributeError
print(A.p)  # <property object at 0x0000015F14652630>

class BMeta(type):
    def __getattr__(self, name):
        return f'metaclass __getattr__ {name}'


class B(metaclass=BMeta):
    @types.DynamicClassAttribute
    def d(self):
        return 'dynamic_class_attr'


b = B()
print(b.d)  # dynamic_class_attr
print(B.d)  # metaclass __getattr__ d
```



eum.Enum 当該箇所のコード(Python 3.11.0)[^enum_property]: [https://github.com/python/cpython/blob/v3.11.0/Lib/enum.py#L1221-L1229](https://github.com/python/cpython/blob/v3.11.0/Lib/enum.py#L1221-L1229)

object.\_\_getattr\_\_ の公式ドキュメント: [https://docs.python.org/ja/3.11/reference/datamodel.html?highlight=\_\_getattr\_\_#object.\_\_getattr\_\_](https://docs.python.org/ja/3.11/reference/datamodel.html?highlight=__getattr__#object.__getattr__)

言語リファレンス - データモデル - メタクラス: [https://docs.python.org/ja/3.11/reference/datamodel.html#metaclasses](https://docs.python.org/ja/3.11/reference/datamodel.html#metaclasses)

[^enum_property]: このコードの property はビルトインのではなく class property(DynamicClassAttribute) と定義されている enum ライブラリ独自のもの

## coroutine
ジェネレータ関数として書かれた旧式のコルーチンを、 async def で作られる現行のコルーチン関数に変換するユーティリティーです。 Python 3.4 時代のコードの変換に用いられるもので、 2022 年現在、新たに使うことはないものです。

## おわりに
汎用的名前をしているうえに、複数の方向性をもつ関数が寄せ集められているのでつかみどころがない標準ライブラリ [types][link-types]でしたが紹介してみました。ぜひ、つかってあげてください。