본문으로 건너뛰기
김신건의 로그

[Python] collections.abc: 추상 컬렉션 인터페이스

· 수정 · 📖 약 1분 · 471자/단어 #python #collections #abc #protocol #stdlib
python collections.abc, Iterable, Sequence, Mapping, Set, MutableSequence, abc

정의

collections.abc컬렉션의 추상 베이스 클래스를 제공한다. 사용자 정의 컬렉션이 빌트인과 같은 인터페이스를 구현했음을 명시하거나, isinstance 검사에 사용. PEP 3119.

타입 힌트에서도 자주 보임 (Sequence[int]list[int]보다 일반적).

주요 ABC 계층

Container          __contains__
Hashable           __hash__
Iterable           __iter__
Iterator           __next__, __iter__       ← Iterable
Reversible         __reversed__              ← Iterable
Sized              __len__
Callable           __call__
Collection         __contains__, __iter__, __len__   ← Iterable, Container, Sized

Sequence           __getitem__, __len__              ← Reversible, Collection
                   index, count, __iter__, __contains__, __reversed__
MutableSequence    __setitem__, __delitem__, insert  ← Sequence
                   append, extend, pop, remove, clear

Set                __contains__, __iter__, __len__   ← Collection
                   __le__, __lt__, __eq__, __ne__, __gt__, __ge__
                   __and__, __or__, __sub__, __xor__, isdisjoint
MutableSet         add, discard                       ← Set
                   clear, pop, remove

Mapping            __getitem__, __len__, __iter__    ← Collection
                   __contains__, keys, items, values, get, __eq__, __ne__
MutableMapping     __setitem__, __delitem__          ← Mapping
                   pop, popitem, clear, update, setdefault

MappingView        __len__
ItemsView          __iter__, __contains__            ← MappingView, Set
KeysView           __contains__, __iter__            ← MappingView, Set
ValuesView         __iter__, __contains__            ← MappingView

Awaitable          __await__
Coroutine          send, throw, close                 ← Awaitable
AsyncIterable      __aiter__
AsyncIterator      __anext__, __aiter__              ← AsyncIterable
AsyncGenerator     asend, athrow                     ← AsyncIterator

isinstance 검사

from collections.abc import Iterable, Sequence, Mapping

isinstance([1, 2, 3], Iterable)     # True
isinstance("abc", Iterable)          # True
isinstance({"a": 1}, Iterable)       # True (키 순회)
isinstance(42, Iterable)             # False

isinstance([1, 2, 3], Sequence)     # True
isinstance({1, 2, 3}, Sequence)     # False (순서 없음)
isinstance("abc", Sequence)          # True
isinstance(b"abc", Sequence)         # True

isinstance({}, Mapping)             # True
isinstance({}.keys(), Set)           # True (set처럼 다룰 수 있음)

빌트인 타입들은 자동으로 적절한 ABC에 등록됨.

타입 힌트에 활용

함수 인자를 구체 타입(list, dict)으로 제한하지 말고 ABC로.

from collections.abc import Iterable, Mapping, Sequence

def sum_all(xs: Iterable[int]) -> int:
    return sum(xs)
# list, tuple, set, generator 모두 받음

def show(items: Sequence[str]) -> None:
    for i, x in enumerate(items):
        print(i, x)
# list, tuple만 (인덱싱 가능)

def update_settings(s: Mapping[str, int]) -> None:
    for k, v in s.items():
        ...
# dict뿐 아니라 임의 mapping

라이브러리 함수는 가능한 한 일반적 ABC. 호출자가 다양한 타입을 자유롭게 넘길 수 있다.

typing.Iterable 등은 collections.abc.Iterable의 별칭 (3.9+).

직접 컬렉션 만들기

ABC를 상속해 일부 메서드만 구현하면 나머지가 자동 제공.

from collections.abc import MutableSequence

class IntList(MutableSequence):
    def __init__(self):
        self._items = []

    def __getitem__(self, i):
        return self._items[i]

    def __setitem__(self, i, v):
        if not isinstance(v, int):
            raise TypeError("int only")
        self._items[i] = v

    def __delitem__(self, i):
        del self._items[i]

    def __len__(self):
        return len(self._items)

    def insert(self, i, v):
        if not isinstance(v, int):
            raise TypeError("int only")
        self._items.insert(i, v)

# append, extend, pop, remove, clear, reverse, __contains__, __iter__,
# index, count 등은 ABC가 자동 제공

lst = IntList()
lst.append(1)
lst.append(2)
lst.extend([3, 4])
print(list(lst), len(lst))     # 자동
lst.remove(2)

abstractmethod만 구현하면 나머지가 무료. 다만 효율적인 직접 구현이 더 나을 수 있음.

가상 서브클래스 (register)

명시적 상속 없이 ABC 호환 표현.

from collections.abc import Sized

class MyContainer:
    def __init__(self):
        self.size = 0
    def __len__(self):
        return self.size

Sized.register(MyContainer)
isinstance(MyContainer(), Sized)    # True

또는 __subclasshook__로 자동 등록 (모든 ABC가 이 방식 사용).

Hashable의 의미

from collections.abc import Hashable

isinstance("abc", Hashable)    # True
isinstance((1, 2), Hashable)   # True
isinstance([1, 2], Hashable)   # False (mutable)
isinstance({1, 2}, Hashable)   # False
isinstance(frozenset(), Hashable)    # True

__hash__가 None이면 Hashable 아님. 가변 컨테이너 검사에 활용.

비교: typing vs collections.abc

# typing (3.9 이전)
from typing import Iterable, List, Dict
def f(xs: Iterable[int]) -> List[int]: ...

# 3.9+ collections.abc + 내장 generic
from collections.abc import Iterable
def f(xs: Iterable[int]) -> list[int]: ...

3.9+에선 typing.Iterable 대신 collections.abc.Iterable이 표준.

자주 보는 활용

duck typing 명시

def process(items):
    if isinstance(items, Mapping):
        for k, v in items.items():
            handle(k, v)
    elif isinstance(items, Iterable):
        for x in items:
            handle(x)
    else:
        raise TypeError

dict이냐 list이냐 같은 구체 검사보다 인터페이스로.

dict의 view를 set처럼

a = {"x": 1, "y": 2}
b = {"y": 99, "z": 3}

a.keys() & b.keys()    # {"y"}  (set 연산이 가능!)

dict.keys()는 KeysView (Set의 서브클래스). 자연스럽게 집합 연산.

Mapping 인터페이스 구현

from collections.abc import Mapping

class CaseInsensitiveDict(Mapping):
    def __init__(self, data=None):
        self._data = {}
        if data:
            for k, v in dict(data).items():
                self[k.lower()] = v

    def __setitem__(self, k, v):
        self._data[k.lower()] = v

    def __getitem__(self, k):
        return self._data[k.lower()]

    def __iter__(self):
        return iter(self._data)

    def __len__(self):
        return len(self._data)

MutableMapping을 상속하면 update, pop, setdefault, items, keys, values, get 등 모두 무료.

함정

1. Sequence ≠ Iterable

def f(xs: Sequence[int]) -> int:
    return xs[0]    # 인덱싱 필요

# generator는 Iterable이지만 Sequence는 아님
f(x for x in [1, 2, 3])    # 타입 체커 에러 또는 런타임 인덱싱 실패

인덱싱이 필요하면 Sequence. 한 번 순회만이면 Iterable.

2. Set 부분집합 검사

{1, 2} <= {1, 2, 3}    # True (Set은 < <= 비교 정의)

list와 다른 의미라 헷갈리지 말 것.

3. dict의 itertools 호환

dict.items()는 Iterable이라 itertools.chain, map 등에 전달 가능.

import itertools
list(itertools.chain(d1.items(), d2.items()))

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기