2021-04-01に投稿

Python 標準ライブラリ collections コンテナデータ型

読了目安:12分

組み込み型 list、 tuple、 dict、 set以外で使えるコンテナのデータ型です。

概要

以下のようなコンテナデータ型・関数が用意されています。

  • namedtuple() 名前付きタプル
  • Counter キーによる数え上げ
  • ChainMap 複数の辞書から1つの辞書を構成
  • OrderedDict 追加順序ありの辞書
  • defaultdict 既定値ありの辞書
  • deque 両端で出し入れするキュー
  • UserDict 辞書のサブクラス作成用
  • UserList リストのサブクラス作成用
  • UserString 文字列のサブクラス作成用

namedtuple() 名前付きタプル

namedtupleの定義

from collections import namedtuple

# namedtuple(typename, field_names, *, rename=False, defaults=None, module=None)
# typename : タプルにつける名称
# field_names : フィールド名のリスト、または、空白やカンマ区切りの文字列
# rename : 名称の自動修正を行う場合True
# defaults : デフォルト値のiterable
# module : __module__属性
PersonRecord = namedtuple('PersonRecord', ['name', 'age', 'departmnet'])
PersonRecord = namedtuple('PersonRecord', 'name, age, departmnet')
PersonRecord = namedtuple('PersonRecord', 'name age departmnet')

raname=Trueで識別子として使えない名称が自動で置換される。

typename = namedtuple('typename', 'namedummy, def, namedummy, int', rename=True)
typename._fields # => ('namedummy', '_1', '_2', 'int')

defaultsで既定値を設定できる。

DefautlSample = namedtuple('DefautlSample', 'f1, f2, f3', defaults=['X', 1])
DefautlSample._field_defaults # => {'f2': 'X', 'f3': 1}

ds = DefautlSample('A')
ds # = > DefautlSample(f1='A', f2='X', f3=1)

namedtupleの作成は、位置で引数を渡すほか、
辞書を**で展開して引数として渡すこともできる。

PersonRecord = namedtuple('PersonRecord', 'name, age, departmnet')
dic = {'name':'Taro', 'age':22, 'departmnet':'A1'}
pr = PersonRecord(**dic)
pr # => PersonRecord(name='Taro', age=22, departmnet='A1')

namedtupleの使い方と要素へのアクセス

PersonRecord = namedtuple('PersonRecord', ['name', 'age', 'departmnet'])
item = PersonRecord('Taro', 22, departmnet='A1')
item # => PersonRecord(name='Taro', age=22, departmnet='A1')

name , age , departmnet = item
name # => 'Taro'
age # => 22
departmnet # => 'A1'

item[0] # => 'Taro'
item[1] # => 22
item[2] # => 'A1'

item.name # => 'Taro'
item.age # => 22
item.departmnet # => 'A1'

getattr(item , 'name') # => 'Taro'
getattr(item , 'age') # => 22
getattr(item , 'departmnet') # => 'A1'

namedtupleの属性・メソッド

# _fields フィールド名のタプル
item._fields # => ('name', 'age', 'departmnet')

# _asdict() 辞書の取得
item._asdict() # => {'name': 'Taro', 'age': 22, 'departmnet': 'A1'}

# _make(iterable) インスタンスの生成
instance = PersonRecord._make(['Jiro', 20, 'B1'])
instance #=> PersonRecord(name='Jiro', age=20, departmnet='B1')

# _replace(**kwargs) 書き換えたタプルの取得
instance._replace(age=19) # => PersonRecord(name='Jiro', age=19, departmnet='B1')

namedtupleの使用例(SQLite)

DB接続等のマッピングで便利に使える。

import sqlite3
from contextlib import closing
from collections import namedtuple

PersonRecord = namedtuple('PersonRecord', 'name, age, departmnet')

with closing(sqlite3.connect('sample.db')) as con:
    cursor = con.cursor()
    cursor.execute("""
        CREATE TABLE IF NOT EXISTS person
        (name TEXT, age INTEGER, departmnet TEXT)
    """)
    cursor.execute("DELETE FROM person ;")
    cursor.execute("INSERT INTO person VALUES ('Taro',   25, 'A1' );")
    cursor.execute("INSERT INTO person VALUES ('Jirp',   20, 'B1' );")
    cursor.execute("INSERT INTO person VALUES ('Hanako', 22, 'A2' );")
    con.commit()

    cursor.execute("SELECT name, age, departmnet from person;")

    for person in map(PersonRecord._make, cursor.fetchall()):
        print(person)
        print(person.name, person.age, person.departmnet)

    con.close()

Counter キーによる数え上げ

Counterの定義

from collections import Counter

c = Counter()
c # => Counter()

c = Counter('ababcbacabacbacada')
c #=> Counter({'a': 8, 'b': 5, 'c': 4, 'd': 1})

c = Counter(['AAA', 'ABA', 'ABA', 'AAA', 'AAA', 'BAA', 'AAA', 'AAA', 'ABA'])
c # => ounter({'AAA': 5, 'ABA': 3, 'BAA': 1})

c = Counter({'key1': 5, 'key2': 7}) 
c # => Counter({'key1': 5, 'key2': 7})

c = Counter(key1=5, key2=7) 
c # => Counter({'key1': 5, 'key2': 7})

Counterの属性・メソッド

c = Counter('ababcada')

# elements キーのカウント数繰り返し
list(c.elements()) # => ['a', 'a', 'a', 'a', 'b', 'b', 'c', 'd']

# most_common([n]) カウント上位のキーと数のタプル
c.most_common(3) # => [('a', 4), ('b', 2), ('c', 1)]

# subtract([iterable-or-mapping]) 差分
c1 = Counter(a=3, b=2, c=3)
c2 = Counter(a=1, b=2, c=5)
c1.subtract(c2)
c1 # => Counter({'a': 2, 'b': 0, 'c': -2})

Counterの使い方

c = Counter('ababcada')
c # => Counter({'a': 4, 'b': 2, 'c': 1, 'd': 1})

# Counterはdictのサブクラス
isinstance(c , dict) # => True

c.values() # => dict_values([4, 2, 1, 1]) カウントのリスト
sum(c.values()) # => 8 カウントの合計

c.update({'a':2,'c':1}) # updateはカウントの加算
c # => Counter({'a': 6, 'b': 2, 'c': 2, 'd': 1})

ChainMap 複数の辞書から1つの辞書を構成

ChainMapの定義

from collections import ChainMap
map1 = {'k1':'value1_1', 'k2':'value2_1', 'k3':'value3_1'}
map2 = {'k2':'value2_2', 'k4':'value4_2'}

cm = ChainMap(map2, map1)

ChainMapの属性・メソッド

map1 = {'k1':'value1_1', 'k2':'value2_1', 'k3':'value3_1'}
map2 = {'k2':'value2_2', 'k4':'value4_2'}

cm = ChainMap(map2, map1)

# maps マッピングの一覧
cm.maps
# => {'k2': 'value2_2', 'k4': 'value4_2'}, {'k1': 'value1_1', 'k2': 'value2_1', 'k3': 'value3_1'}]

# リストは全ての世代のキー
list(cm) # => ['k1', 'k2', 'k3', 'k4']

for k in cm:
    print(f'cm["{k}"]={cm[k]}')
# => 
# cm["k1"]=value1_1
# cm["k2"]=value2_2
# cm["k3"]=value3_1
# cm["k4"]=value4_2

# new_child(m=None) 新しい辞書を追加したChsainMapを取得
map3 = {'k3':'value3_3', 'k5':'value5_3'}
cm_new = cm.new_child(map3)

for k in cm_new:
    print(f'cm_new["{k}"]={cm_new[k]}')
# => 
# cm_new["k1"]=value1_1
# cm_new["k2"]=value2_2
# cm_new["k3"]=value3_3
# cm_new["k4"]=value4_2
# cm_new["k5"]=value5_3

# parents 最初の辞書以外の全辞書を持ったChainMap
cm_new.parents == cm # => True
for k in cm_new.parents:
    print(f'cm_new.parents["{k}"]={cm_new.parents[k]}')
# => 
# cm_new.parents["k1"]=value1_1
# cm_new.parents["k2"]=value2_2
# cm_new.parents["k3"]=value3_1
# cm_new.parents["k4"]=value4_2

# 更新前の辞書
cm_new.maps
# =>
# [{'k3': 'value3_3', 'k5': 'value5_3'},
#  {'k2': 'value2_2', 'k4': 'value4_2'},
#  {'k1': 'value1_1', 'k2': 'value2_1', 'k3': 'value3_1'}]

# 値の更新
cm_new['k2'] = 'value2_new'
cm_new['k5'] = 'value5_new'
del cm_new['k3']

# 辞書の更新は最新の辞書について行われる
cm_new.maps
# =>[{'k5': 'value5_new', 'k2': 'value2_new'},
#  {'k2': 'value2_2', 'k4': 'value4_2'},
#  {'k1': 'value1_1', 'k2': 'value2_1', 'k3': 'value3_1'}]

OrderedDict 追加順序ありの辞書

OrderedDictの定義

from collections import OrderedDict

od = OrderedDict({'a':1, 'b':2, 'c':3})

OrderedDictの属性・メソッド

from collections import OrderedDict

od = OrderedDict({'a':1, 'b':2, 'c':3})
od['e'] = 4
od['f'] = 5
od # => OrderedDict([('a', 1), ('b', 2), ('c', 3), ('e', 4), ('f', 5)])

isinstance(od, dict) # => True

# popitem(last=True) last:TrueならLIFO、falseならFIFO
pi = od.popitem()
pi # => ('f', 5)
od # => OrderedDict([('a', 1), ('b', 2), ('c', 3), ('e', 4)])

# move_to_end(key, last=True) キーの項目を辞書の最初から最後に移動
od.move_to_end('c')
od #=> OrderedDict([('a', 1), ('b', 2), ('e', 4), ('c', 3)])
od.move_to_end('b',last=False)
od # => OrderedDict([('b', 2), ('a', 1), ('e', 4), ('c', 3)])

list(reversed(od)) # => ['c', 'e', 'a', 'b']

defaultdict 既定値ありの辞書

defaultdictの定義

from collections import defaultdict

# defaultdict([default_factory[, ...]]) デフォルト値を設定し初期化
dd = defaultdict() # => defaultdict(None, {})
dd = defaultdict(int) # => defaultdict(int, {})
dd = defaultdict(list) # => defaultdict(list, {})

defaultdictの使用例

dd = defaultdict(int) # => defaultdict(list, {})

for s in 'acababcaa':
    dd[s] += 1

dd # => defaultdict(int, {'a': 5, 'c': 2, 'b': 2})

deque 両端で出し入れするキュー

dequeの定義

from collections import deque

# deque([iterable[, maxlen]])
deq = deque() # => deque([])
deq = deque('aabacab') # => deque(['a', 'a', 'b', 'a', 'c', 'a', 'b'])
deq = deque([1, 3, 2, 5, 1]) # => deque([1, 3, 2, 5, 1])
deq = deque([1, 3, 2, 5, 1], 3) # => eque([2, 5, 1])

dequeの属性・メソッド

deq = deque() # => deque([])

# append(x) 右側追加
deq.append(1) # => deque([1])
deq.append(2) # => deque([1, 2])

# appendleft(x) 左側追加
deq.appendleft(3) # => deque([3, 1, 2])

# extend(iterable) 右にイテレータの要素を追加
deq.extend([1, 1, 1, 3, 2]) # => deque([3, 1, 2, 1, 1, 1, 3, 2])

# extendleft(iterable) 左にイテレータの要素を追加
deq.extendleft([3, 2]) # => deque([2, 3, 3, 1, 2, 1, 1, 1, 3, 2])

# insert(i, x) 挿入
deq.insert(5, 3) # => deque([2, 3, 3, 1, 2, 3, 1, 1, 1, 3, 2])

# count(x) 要素の数え上げ
deq.count(1) # => 4

# index(x[, start[, stop]]) 位置の取得
deq.index(1) # => 3
deq.index(1, 4) # => 6
# deq.index(1, 1, 2) # => ValueError: 1 is not in deque

# copy() シャローコピー
copy_deq = deq.copy()
copy_deq # => deque([2, 3, 3, 1, 2, 3, 1, 1, 1, 3, 2])

# pop() 右から取得
item = deq.pop()
item # => 2
deq # => deque([2, 3, 3, 1, 2, 3, 1, 1, 1, 3])

# popleft() 左から取得
item = deq.popleft()
item # => 2
deq # => deque([3, 3, 1, 2, 3, 1, 1, 1, 3])

# remove(value) 最初に現れる要素の除外
deq.remove(1)
deq # => deque([3, 3, 2, 3, 1, 1, 1, 3])

# reverse() 順番の反転
deq.reverse()
deq # => deque([3, 1, 1, 1, 3, 2, 3, 3])

# rotate(n=1) 右にローテーション(マイナスで左)
deq.rotate(2)
deq # => deque([3, 3, 3, 1, 1, 1, 3, 2])

# maxlen 最大長の制限
deq.maxlen # => None

# clear()
deq.clear()
deq # => deque([])
Originally published at marusankakusikaku.jp
ツイッターでシェア
みんなに共有、忘れないようにメモ

maru3kaku4kaku

Pythonこつこつ学習中。よく忘れる。

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント