Bits

me: 8バイトなんだけど56bit、なぜなら入力がUS-ASCII限定で最上位ビットが0だからそこは詰めてもかまわない、と。詰めないでくれたらだいぶ楽なのに!!
me: 1バイトの節約とかやめてくれ!w
me: きっと組み込み脳とLL脳とでそのうち血で血を洗う戦争が発生する。
me: Pythonでビットを簡単に扱えるようにするためのライブラリはないのか
me: 「このバイト列から7ビットずつ取り出す」

というわけで書いた

def to_bin(x, width=8):
    """
    for debug
    >>> to_bin('A')
    '01000001'
    >>> to_bin('AB')
    '01000001,01000010'
    """
    if isinstance(x, str):
        if len(x) == 1:
            return to_bin(ord(x))
        return ",".join(map(to_bin, x))
    if isinstance(x, int):
        return "".join(
            str(int(bool(x & (2 ** i))))
            for i in reversed(range(width)))
    raise NotImplementedError

def split_to_fixed_bits(bytes, width):
    """
    >>> to_bin("ABC")
    '01000001,01000010,01000011'
    >>> [to_bin(x, 4) for x in split_to_fixed_bits("ABC", 4)]
    ['0100', '0001', '0100', '0010', '0100', '0011']
    >>> [to_bin(x, 3) for x in split_to_fixed_bits("ABC", 3)]
    ['010', '000', '010', '100', '001', '001', '000', '011']
    """
    if width > 8: raise NotImplementedError
    mask = (2 ** width) - 1
    total = len(bytes) * 8
    if total % width: 
        bytes += "\x00"
    start = 0
    for start in range(0, total, width):
        sb = start / 8
        end = start + width
        eb = (end - 1) / 8
        if end % 8 == 0:
            yield ord(bytes[sb]) & mask
            
        elif sb == eb:
            ret = ord(bytes[sb])
            _mask = mask << (8 - end % 8)
            ret &= _mask
            ret >>= 8 - end % 8
            yield ret

        else:
            before_half = ord(bytes[sb])
            _mask = (2 ** (8 - start % 8)) - 1
            before_half &= _mask
            before_half <<= end % 8
            after_half = ord(bytes[eb])
            _mask = (2 ** (end % 8)) - 1
            _mask <<= 8 - end % 8
            after_half &= _mask
            after_half >>= 8 - end % 8
            yield before_half + after_half

というわけで個人的にはもうパズルをとき終わった気満点でこのままぽいっとしてしまいそうだけど、CodeReposに入れた方がいいのかな。入れた方がいいと思う人ははてなスターをぽちっと(ぉ