3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

KLab EngineerAdvent Calendar 2022

Day 6

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

Last updated at Posted at 2022-12-05

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

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

組み込み型へのアクセス

組み込みインスタンス None の型は? 関数オブジェクトの型は? types ライブラリはそういった「builtin 空間に置くほどではない、しかしそれでもインタプリタが使用していて存在している型」へのアクセスのための口であります。

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

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

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

MappingProxyType 読み出し専用のマッピングのプロキシ

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

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

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

SimpleNamespace 名前空間の作成

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

>>> 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, typing.NamedTuple のほうが適しています。

new_class 動的な型生成

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

>>> 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

DynamicClassAttribute クラス属性のカスタマイズ

property と同じように使えるデコレータ、それが types.DynamicClassAttribute です。 property との違いは、クラス越しにアクセスされたときに AttributeError を送出することです。このエラーで __getattr__ メソッドの呼び出しを促すことにより、インスタンス越しの属性アクセスとクラス越しでの属性アクセスの結果を出しわける実装が可能になります。 enum.Enum の name, value の機構で使われているものです。

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)1: 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#metaclasses

coroutine

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

おわりに

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

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

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?