Python 3 varsの応用

Python 3.xの組み込み関数`vars`の応用的なコード集です。

## この投稿の方針

- [タプル`tuple`はインデクスとシークエンスのアンパック（アンパック代入）の2通りの方法で分解できます](https://qiita.com/katabamisan/items/9f8b78d732068ba5a4a9)。この投稿では後者を優先します。

```py
dict1 = {"a": "a", "b": "b", "c": "c"}
# タプルのまま受け取ってインデクサ
{item[0]: item[1] for item in dict1}
# アンパック代入
{key: value for key, value in dict1}

```

## 辞書内包記法でコピーする

`vars`の戻り値は辞書なので`items()`と辞書内包記法でコピーできます。`items()`の要素はキーと値のタプルです。

```py
import sys
from pprint import pp

dict1 = {key: value for key, value in vars(sys).items()}
pp(dict1)
```

```plain:出力
{'__name__': 'sys',
 '__doc__': 'This module provides access to some objects ...,
 ...
```

注意：`vars`組み込み関数の戻り値は辞書型（`<class 'dict'>`）です。要素（キーと値のタプル）を列挙するには`vars(sys).items()`とする必要があります。`for item in vars(sys)`とした場合は`vars(sys).keys()`として扱われ、キーの1文字目をキー、2文字目を値とした辞書が作成されます。アンパックを使用する場合はエラーが発生するので気づきます。

## キーと値の型の辞書を作成する

```py
import sys
from pprint import pp

dict1 = {key: type(value) for key, value in vars(sys).items()}
pp(dict1)
```

```plain:出力
{'__name__': <class 'str'>,
 '__doc__': <class 'str'>,
 '__package__': <class 'str'>,
 '__loader__': <class 'type'>,
 ...
```

## 値の型を列挙する

```py
import sys
from pprint import pp

set1 = {type(value).__name__ for value in vars(sys).values()}
pp(set1)
```

```plain:出力
{'ModuleSpec',
 'NoneType',
 'SimpleNamespace',
 'TextIOWrapper',
 'bool',
 'builtin_function_or_method',
 'dict',
 'flags',
 'float_info',
 'function',
 'hash_info',
 'int',
 'int_info',
 'list',
 'str',
 'thread_info',
 'tuple',
 'type',
 'version_info'}
```

- 型の名前は`type(...).__name__`で取得できます。
- `{e for e in v}`は集合内包記法です。

## 値の型の出現回数を数える

```py

import sys
from collections import Counter
from pprint import pp

counter = Counter((type(value).__name__ for value in vars(sys).values()))
pp(counter)
```

```plain:出力
Counter({'builtin_function_or_method': 42,
         'str': 16,
         'TextIOWrapper': 6,
         'int': 5,
         'list': 5,
         'dict': 3,
         'tuple': 2,
         'NoneType': 2,
         'type': 1,
         'ModuleSpec': 1,
         'float_info': 1,
         'int_info': 1,
         'hash_info': 1,
         'version_info': 1,
         'SimpleNamespace': 1,
         'flags': 1,
         'thread_info': 1,
         'bool': 1,
         'function': 1})
```

## 組み込み関数・メソッドのみ取得する
```py
import sys
import types
from pprint import pp

dict1 = {key: value for item in vars(sys).items()
         if isinstance(value, types.BuiltinFunctionType)}
pp(dict1)
```

```plain:出力
{'addaudithook': <built-in function addaudithook>,
 'audit': <built-in function audit>,
 'breakpointhook': <built-in function breakpointhook>,      
 'callstats': <built-in function callstats>,
 '_clear_type_cache': <built-in function _clear_type_cache>,
 ...
```

## 組み込み関数・メソッド以外を取得する

```py
import sys
import types
from pprint import pp

dict1 = {key: value for item in vars(sys).items()
         if not isinstance(value, types.BuiltinFunctionType)}
pp(dict1)
```

```plain:出力
{'__name__': 'sys',
 '__doc__': 'This module provides ...',
 '__package__': '',
 '__loader__': <class '_frozen_importlib.BuiltinImporter'>,
 '__spec__': ModuleSpec(name='sys', loader=<class '_frozen_importlib.BuiltinImporter'>),
 ...
```

## 値を適当な長さに切り詰める
```py
import sys
import types
from pprint import pp
from typing import Optional


def str_truncate_singleline(s, width: int, linesep: Optional[str]):
    s = str(s)
    if not isinstance(width, int):
        raise TypeError()
    addsEllipsis: bool = False
    if linesep is not None:
        i: int = s.find(linesep)
        if i != -1:
            s = s[0:i]
            addsEllipsis = True
    if len(s) + len("...") > width:
        return s[0:width-len("...")] + "..."
    return s + "..." if addsEllipsis else s


dict1 = {key: str_truncate_singleline(value, 50, "\n")
         for item in vars(sys).items()
         if not isinstance(value, types.BuiltinFunctionType)}
pp(dict1)
```