What's New In Python3.0 日本語版

What'sNewInPython3.0 - TSNETWiki on TextWorld

本文はGuido van Rossumの"What's New In Python 3.0"( http://docs.python.org/3.0/whatsnew/3.0.html )の全訳です。

すばらしい(拍手)



気になったところはここに書きためていって、ある程度まとまってからフィードバックしようっと。



(訳注:原文では『キーワード(keyword)』と書かれているが、『予約語(reserved word)』の誤りと思われる。Pythonでは、キーワードは、引数などを指定する名前(キーワード引数)などに使われ、他の言語のように予約語の意味として使われることはほとんどない)

「誤り」ってのは言い過ぎかと。単語の定義はReference Manualを参照するのが筋。
http://docs.python.org/reference/lexical_analysis.html#identifiers-and-keywords

The following identifiers are used as reserved words, or keywords of the language

というわけでPythonでも「予約語」と「キーワード」は同じ意味ですよと。見出し自体Identifiers and Keywordsだし。

ただもちろん「キーワード引数」にも「キーワード」が使われていて混乱の元なので予約語の意味の「キーワード」は「予約語」と訳してしまっていいかなと思います。もしくは「キーワード(予約語)」か「予約語(キーワード)」か。

『ロング整数』のreprでは、後ろに'L'をつけません。従って、無条件に、そのキャラクタを取り除くコードは代わりに最終桁を切り落としてしまうでしょう。(訳注:reprはrepr関数での出力を意味するとともに、オブジェクトの文字表現を意味する。repr = representation)

要するに「longの文字列表現はもはや最後にLがつかないので、repr(x)[:-1]なんかで最後の文字を(Lかどうかチェックもせずに)取り除いているようなコードは1の位が欠けた表示になってしまいますよ」ということ。最後のLを取り除きたいならrepr(x).rstrip("L")するべきなんだけど、そうしてないケースも多いんだろうな。

あ「strを使え」と書いてあるのは言葉足らずだけどPython3.0でstrを使えという意味ではなくて、2.*でもstrを使えばLの付いていない文字列が得られますよ、という意味。


特に呼び出した関数に副作用を引き起こすmap関数の使用は、邪道です。正しい変換は、普通のループを使用することです(どうせリストを作成するだけ無駄でしょうから)

えっと、原文は

Particularly tricky is map() invoked for the side effects of the function; the correct transformation is to use a regular for loop (since creating a list would just be wasteful).

なので「呼び出した関数に副作用を引き起こすmap関数の使用」だと誤訳ですね。「関数の副作用を引き起こすためにmapを使うこと」なわけだけども、読者が「副作用」とは何なのかをわかっていないと伝わらないので英語の原文自体があんまりフレンドリーじゃないよね。ようするにmap(print, [1, 2, 3])なんて書き方はトリッキーなので素直に

for x in [1, 2, 3]:
    print(x)

と書いた方がいいよというお話。前者では一時的に[None, None, None]というリストが生成されることになるので無駄だし。


とここまでは原文にかかれている説明だけども論点がおかしいよね。副作用がある関数を使うことと、結果として作られるリストが無駄ということは独立な話。注意喚起すべきなのは2.*では上のmapを使うコードとforを使うコードは同じ動きをするけども3.0ではそうではないってところじゃないだろうか。つまり、mapがイテレータを返すようになったことで、関数の評価が結果が必要とされるまで遅延するようになったわけだけど、上のトリッキーな書き方をしているコードではmapの結果を必要としないので本来起きるべきだった副作用がまるっと抜けたまま走り続けてしまう。副作用があるかないかを判定しようとすると実装に大幅に手を入れないと行けないからめんどくさいし、そもそもmapをそんな目的に使うのは嫌いなので「トリッキーな書き方だからダメなんだ」ということにしたいわけですね。


などとぐだぐだ言いつつ、僕が訳すのならこう書くかな:

特にトリッキーなのはmap()を副作用のある関数の副作用を引き起こすために使うことです。これは普通のfor文に変えるべきです。

僕が訳すと「原文が悪い」とか「これじゃわかりにくいからコード例を出すべきだ」とか言い出して出来上がりが別物になってしまうのが問題点だ。



『!=』は、『==』がNotImplemented?を返さない限り、『==』の反対を返すようになりました。

これ「NotImplemented例外を投げない限り」だよね。返したらTrueとみなされるので!=はFalseになってしまう、と思ったけど違った。比較演算子がNotImplementedを返した場合は比較方法が定義されていないって意味になるんだな。NotImplementedErrorと紛らわしいけど全然別物なので要注意。

"""                                                                                                               
>>> Return() != Return()                                                                                          
True                                                                                                              
>>> ReturnError() != ReturnError()                                                                                
False                                                                                                             
>>> Raise() != Raise()                                                                                            
Traceback (most recent call last):                                                                                
    ...                                                                                                           
NotImplementedError                                                                                               
"""

class Return(object):
    def __eq__(self, v):
        return NotImplemented

class ReturnError(object):
    def __eq__(self, v):
        return NotImplementedError

class Raise(object):
    def __eq__(self, v):
        raise NotImplementedError

def _test():
    import doctest
    doctest.testmod()

_test()



「古いprint文の特殊な文法は廃止され、print関数(とキーワード引数)に置き換えられました。例:」の下の例に原文にない空白が入っているのはもしかしてそのまま書くとWiki記法に引っかかるからだろうか。


sorted組み込み関数とlist.sortメソッドは、もはや『cmp』を引数に取ることはできません。代わりに『key』という名称のキーワード引数に(比較関数を)渡します。

おっと「『key』という名称のキーワード引数に(比較関数を)渡します。」は蛇足。西尾泰和のブログ @ Cybozu Labs: Python2.4以降での高速なソートを参照。簡単に説明すると「比較関数を渡すことでソート方法をカスタマイズ」という発想自体があんまり計算量的によろしくないものなので「ソートするべき値のリストを作る関数」を渡す方針(Schwartzian transform - Wikipedia, the free encyclopedia)に変更したということです。

例を挙げると、ソートすべきリストが大きいときには大文字小文字区別せずにソートするのにxs.sort(cmp=lambda x, y: cmp(x.lower(), y.lower())って書くんじゃなくてxs.sort(key=lambda x:x.lower())やそれと同じ働きのxs.sort(key=str.lower)を使った方が桁違いに速いです。


すべてのテキストはUnicodeです。しかしながら、エンコードされたUnicodeはバイナリデータとして表現されます。(訳注:PythonUnicodeUTF-8の形で扱う)

ええと。UnicodeUTF-8の形で扱うって…

http://svn.python.org/projects/python/trunk/Include/unicodeobject.h

/* Setting Py_UNICODE_WIDE enables UCS-4 storage.  Otherwise, Unicode
   strings are stored as UCS-2 (with limited support for UTF-16) */

んー、やっぱりそうだよなぁ。UTF-8なんかにするはずがないよなぁ。
文字コードの話はめんどうなのであんまり言及したくないのだけどもUTF-8はバイト列なんだからbytesであって、ここで説明されているユニコードのテキストとは互換性がなく結合もできない型のはず。
A: ユニコードの文字列, B: UTF-8のバイト列, C: asciiのバイト列 という4つのデータがあったときに、site.pyに書かれている「バイト列を自動的にユニコードに変換するときのエンコーディング」がデフォルトのasciiだと、A + Cが問題なく動いて、A + Bも実質ascii文字だけ使っている文化圏では問題なく動く。だけどそこに日本語の文字とかが入ってきたらasciiの範囲にないバイトが含まれる可能性があってにっくきUnicodeDecodeErrorが出てしまう。

>>> u"" + "あ"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe3 in position 0: ordinal not in range(128)

ユニコードの文字列とUTF-8(など)のバイト列は別物なのに混同されがちでそのまま結合するコードが書かれやすく、それが自動的に変換されるためエラーにならず、さらにascii圏では正しい挙動をしているように見える」というのが諸悪の根源なので、自動的に変換しないようにしたのが今回の変更。間違えてユニコード文字列にUTF-8バイト列を結合するようなコードを書くとTypeErrorですぐわかる。

>>> "" + "あ".encode("utf-8")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't convert 'bytes' object to str implicitly



Pythonの2.xのモジュールに共通のパターンは、例えばpickleモジュールとcPickleモジュールのように、pure Pythonで実装されたモジュールとともに、オプションとして高速バージョンのCで実装された拡張がある、ということです。このパターンは、高速バージョンを導入しpure Python バージョンに頼る、これらのモジュールを使用するユーザにとって負担になります。Python 3.0では、高速バージョンはpure Python バージョンの実装の詳細とみなされます。高速バージョンを導入し、そしてpure Pythonのバージョンに頼りたい時は、ユーザはいつも標準バージョンを導入すべきです。pickleモジュールとcPickleモジュールのペアは、この処理を受けました。

ええと。

A common pattern in Python 2.x is to have one version of a module implemented in pure Python, with an optional accelerated version implemented as a C extension; for example, pickle and cPickle. This places the burden of importing the accelerated version and falling back on the pure Python version on each user of these modules. In Python 3.0, the accelerated versions are considered implementation details of the pure Python versions. Users should always import the standard version, which attempts to import the accelerated version and falls back to the pure Python version.

これは:

Python2.xではpickleとcPickleのように「pure Pythonで実装されたモジュールがあり、環境によってはC拡張として実装された高速化版もある」というパターンがよくありました。でもこれでは「高速化版を使いたいけど、使えなかったらpure Python版を使う」というユーザにいちいちそのためのコードを書かせることになってしまいます。Python 3.0では、高速化版の存在はユーザから隠すべき実装の詳細と考えられています。ユーザはいつでも標準バージョンをインポートするべきです。標準バージョンは内部で高速化バージョンのインポートを試み、インポートできない場合はpure Python版を使います。

ってこと。cPickleとpickleはすでにそういう変更をうけていて、profileはPython3.1でそうなる予定、StringIOももうioモジュールのクラスになっている、ということ。

「In Python 3.0, the accelerated versions are considered implementation details of the pure Python versions.」っておかしいよね。実装の一部としてC拡張を含んでいる時点でそれはpure Python版とは呼べない。原文が間違っている(また言ってる)


削除の理由は、string.lettersやその仲間がローカル依存の動作を持っていたからです。そしてそれは、そのように魅力的に名付けられたグローバル「定数」のための悪いアイデアです)

globalに釣られたのかもしれないけどロケールをローカルに変えてしまうのはやり過ぎかと思う。ロケールとは 【locale】 - 意味・解説 : IT用語辞典を参照。ここで言われているのは、たとえはlower_lettersって定数は「小文字だな」と思うわけだけど、全角「c」は小文字?キリル文字の小文字である「н」は入れるべきですか?ギリシャ文字の「π」は?っていう問題。だからasciiって名前に含まれるように変更されたわけです。


この場合、主な例外は、二次例外の__cause__属性に格納されています。未処理の例外が__cause__属性と__context__属性の連鎖を渡り歩き、(渡り歩いている連鎖のコンポーネント毎に)分離したトレースバックを出力したときに、最初に最上位の例外を伴ってトレースバックが出力されます

In this case, the primary exception is stored on the __cause__ attribute of the secondary exception. The traceback printed when an unhandled exception occurs walks the chain of __cause__ and __context__ attributes and prints a separate traceback for each component of the chain, with the primary exception at the top.

例外(以下: 一時例外)をキャッチしてexcept節で処理している最中に更に例外がうっかり発生してしまった場合には二つ目の例外(以下: 二次例外)の__context__属性に、自分で明示的に例外を投げ直したときには__cause__属性に最初の例外が入るわけだけど:

処理されない例外が起こったときに表示されるトレースバックは、__cause__や__context__の連鎖をたどっていって、連鎖のコンポーネントごとに分かれたトレースバックを、一時例外が上になるように表示します。

ということ。主語がトレースバックなのは変だなぁ。これって「The traceback printed when an unhandled exception occurs」が主語で「walks」と「prints」が動詞の「SVO and VO 副詞」だという理解であってますかね。

処理されない例外が起こったときには、__cause__や__context__の連鎖をたどっていって、連鎖のコンポーネントごとに分かれたトレースバックを、一時例外が上になるように表示します。

にしたほうが自然かな。日本語らしく主語が曖昧です(ぇ)