Python2.xでは __str__ が unicode を返しても良い
ソースはこれ: http://mail.python.org/pipermail/python-dev/2006-December/070238.html
例えばパスをwrapするオブジェクトを作るときに、パスをunicodeで扱うのであれば、その文字列表現もunicodeにしたい。
そのオブジェクトに文字列表現を与えるとすると
class FooPath(object): def __init__(self, path): self.path = path def __str__(self): return "FooPath('%s')" % (self.path,)
self.path が unicode だと、この __str__() の結果は unicode になってしまう。
In [9]: foo = FooPath(u'foobar') In [10]: foo.__str__() Out[10]: u"FooPath('foobar')"
Bazaarのソースで、 __str__() が unicode を返すのを嫌って、次のようになっていた。
class BarPath(object): def __init__(self, path): assert isinstance(path, unicode) self.path = path def __str__(self): return "BarPath('%s')" % (self.path.encode('utf-8'),) def __unicode__(self): return u"BarPath('%s')" % (self.path,)
しかし、この方式では以下の問題がある。
- print barpath としたときに、sys.stdout.encodingを無視して utf-8 が使われる。
- "%s" % (barpath,) としたときに、ログの出力先のエンコーディングを無視して utf-8 が使われる。 log.warn('%s', barpath) も同じ
後者に関しては u'%s' を使うようにすれば良いが、 "%s" % (u"foo") の結果がunicodeになるのに比べると統一感が無い。
前者に関してはそもそも回避方法が無い。
そこで、 __str__() が unicode を返してもいいものかどうかが悩みどころなんだけれども、上述のとおり良いらしい。ただし、次のような問題が起こるので気をつけないといけない。
In [16]: foo = FooPath(u'\u0342') In [19]: "%s" % (foo,) Out[19]: u"FooPath('\u0342')" In [20]: s = str(foo) --------------------------------------------------------------------------- UnicodeEncodeError Traceback (most recent call last)
この UnicodeEncodeError で誰が悪いのかというと、 unicode かもしれないものに対して str() を呼び出した側が悪い。
str(u"\u0342") もエラーを出すからね。repr()と違って、str()に関してはオブジェクト側が成功する責任を負っていない。
でも、やっぱり既存のコードに手を加えるのには勇気が要るなぁ。