Python3.
Python3.0でのstr/unicode/bytes
第0回でUnicodeの識別子について触れられていましたが、
この関係を端的に言い表しているのが表1で、
C name | 2. |
3. |
3. |
|||
---|---|---|---|---|---|---|
name | repr | name | repr | name | repr | |
PyUnicode | unicode | u'' | str | '' | str | '' |
PyString | str | '' | str8 | s'' | bytes | b'' |
PyBytes | 該当なし | bytes | b'' | bytesarray | bytesarray(b'') |
2点ほど補足。
- Pythonのstr/
bytes/ unicodeはimmutableで、 実装効率の上でmutableなbytesが必要になった場合はbytesarrayを使用します。 - reprはなんらかobjectを、
そのobjectを生成させるcode片を印刷させたいときに使う関数です。この表ではそれぞれのversionのpythonで該当のobjectに対してreprを呼んだときの出力のformatです。2. xでは''がPyStringであったが、 3. 0では''はPyUnicodeを印刷するために使われています。
codecを拡張して「俺様プチencoding」
記事の終わりに今回実装するencodingのソースコードへのリンクを用意してあります。ここではお昼ごはんを食べながらでも読めるように
import codecs
普段、
EGG = b'0x00'
SPAM = b'0xff'
今回作る
def monty_encode(input, error='strict'):
consumed = 0
output = bytearray()
while input:
if input.startswith('egg'):
output.extend(EGG)
read = 3
elif input.startswith('卵'):
output.extend(EGG)
read = 1
elif input.startswith('spam'):
output.extend(SPAM)
read = 4
else:
assert False
consumed += read
input = input[read:]
return output, consumed
いきなりですが、
class Codec(codecs.Codec):
encode = monty_encode
decode = None
インターフェースはcodec.
class IncrementalEncoder(codecs.IncrementalEncoder):
def encode(self, input, final=False):
return monty_encode(input, self.errors)[0]
class IncrementalDecoder(codecs.IncrementalDecoder):
pass
class StreamWriter(Codec,codecs.StreamWriter):
pass
class StreamReader(Codec,codecs.StreamReader):
pass
class StreamConverter(StreamWriter,StreamReader):
encode = None
decode = monty_encode
Streamを使ったEncoder/
def getregentry():
return codecs.CodecInfo(
name='monty',
encode=Codec.encode,
decode=Codec.decode,
incrementalencoder=IncrementalEncoder,
incrementaldecoder=IncrementalDecoder,
streamwriter=StreamWriter,
streamreader=StreamReader,
)
encodingを実装するCodecインスタンスはCodecInfoを使って管理されています
def mysearch(encoding):
if encoding == 'monty':
return getregentry()
else:
return codecs.search_function(encoding)
codecs.register(mysearch)
標準でencodingの名称からcodecを解決するためにregisterされている関数
ioモジュール概要
さて、

詳細は省きますが、
- IOBase継承の頂点にある。
- Bufferされているioの親はBufferedIOBaseである。
- encodingがあるIO classの親はTextIOBaseである。
- on memoryなBufferの実装BytesIOはbytesarrayを使っている。
- 効率が要求されるものはCで実装が提供されている。
ところで、
Note
The standard streams are in text mode by default.
To write or read binary data to these, use the underlying binary buffer.
For example, to write bytes to stdout, use sys.
バッファを用意し、codecをテストする。
テストに使う文字列バッファはencodingとBytesIOの組で実現します。io.
if __name__ == '__main__':
Pythonでは非常によく出てくるトリック。ifでガードされたthen節はコマンドラインでpython [該当のsource file]としたときだけ実行される。テストコードは外部のプログラムからimportする際
import io
raw = io.BytesIO()
buffered = io.BufferedWriter(raw)
x = io.TextIOWrapper(buffered, encoding='monty', errors="strict", newline=None)
メモリ上のバッファとしてのBytesIOのインスタンスを生成し
x.write('egg')
x.flush()
assert x.buffer.raw.getvalue() == EGG
x.write('spamspam')
x.flush()
assert x.buffer.raw.getvalue() == EGG + SPAM + SPAM
x.write('卵')
x.flush()
assert x.buffer.raw.getvalue() == EGG + SPAM + SPAM + EGG
あとはこのバッファに"spam"、