Python3の変更点のメモ
Python3についてはこれまでほとんど目を向けていなかったのだけど、python-devのメーリングリストでPython2系のメンテナンスをどうするかという議論がされているのを見て、改めてPython3について調べてみようと思った。というわけで、Python 3.0のwhat's newを読む。
いろいろ書いてある中で、気になるものをメモ。Python3はapt-get等でインストール後、python3
コマンドで起動できる。ここでは、Python 3.2.3で確認した。
$ python3 Python 3.2.3 (default, Feb 27 2014, 21:33:50) [GCC 4.6.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>>
print文がprint関数に
Python3ではprint文はなくなり、print関数になる。
>>> print("hello world") hello world >>> print("hello world", end="") # 改行しない hello world>>> >>> print("foo", "bar", "baz") # 標準ではスペース (0x20) で区切られる foo bar baz >>> print("foo", "bar", "baz", sep="-") # 区切り文字をハイフンに変更 foo-bar-baz >>> import sys >>> print("hello world", file=sys.stderr) # stderrに出力 hello world
変更の理由はPEP 3105のRationaleの項にある。printを構文として特別扱いする必然性もないので、関数であるべきといったようなところか。
また、Python 3.3ではキーワード引数flushが導入され、Trueを指定することで出力バッファの掃き出しsys.stdout.flush()
を同時に行うことができる。これは便利そう。
文字列フォーマット演算子%
がstr.formatメソッドに
%
はdeprecatedになる。けっこうつらい。
>>> "{} {}".format("hello", "world") 'hello world' >>> "{0} {1} {0}".format("hello", "world") 'hello world hello' >>> "{x} {y} {x}".format(x="hello", y="world") 'hello world hello' >>> "{0[x]} {0[y]}".format({"x": "hello", "y": "world"}) 'hello world' >>> import sys >>> "{0.version}".format(sys) # attributeの参照 '3.2.3 (default, Jul 23 2012, 16:48:24) \n[GCC 4.5.3]'
変更の理由はPEP 3101にある。タプルかdictのどちらかしか与えられないよりは、関数の引数としてキーワードありなしどちらもいけるほうが柔軟だ、ということらしい。
とはいっても、この見慣れない記法はつらい……。こればかりは慣れるしかないのだろう。
いろんなものがlistの代わりにiteratorを返すようになる
dict.items()
、range()
、map()
、zip()
などがlistを返さなくなる。これに合わせて、dict.iteritems()
は使えなくなる。
>>> d = dict(a=2, b=3, c=5) >>> d {'a': 2, 'c': 5, 'b': 3} >>> d.items() dict_items([('a', 2), ('c', 5), ('b', 3)]) >>> d.iteritems() Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'dict' object has no attribute 'iteritems' >>> map(lambda x: x**x, [2,3,5]) <map object at 0xffe2edcc> >>> range(5) range(0, 5) >>> list(range(5)) # listを得る場合には、明示的に`list()`を用いる [0, 1, 2, 3, 4]
range()
やmap()
でlistを取れなくなるのは少しめんどくさい。[i for i in range(5)]
と書くぐらいならlist(range(5))
と書くよなあ……。
あとは、dict.iteritems()
をついつい使ってしまいそう。
strがunicode文字列になる
unicode()
はなくなりstr()
に統一される。また、u""
リテラルはなくなる(ただしPython 3.3では許されるようになった)。バイト列が必要な場合はb""
リテラルを用いることでbytes型として得られる。bytes型はimmutableであるが、bytearray型はmutableである。
また、sys.stdin
、sys.stdout
、sys.stderr
がすべてUnicode文字列で読み書きするようになる。
バイト列を読み書きしたい場合はsys.stdout.buffer.write()
等とする。
reモジュールによる正規表現処理についても注意が必要である。
Python 3では文字クラスがデフォルトでUnicodeのカテゴリとして扱われるため、\d
や\w
が全角文字などにもマッチする。
ASCII文字のみのマッチにするには、明示的にre.ASCIIフラグをつける、あるいは正規表現の先頭に(?a)
をつける必要がある。
>>> import re >>> re.findall(r'\w', 'aàáâãääāăąǎǟǡǻȁȃȧаӑӓᶏḁẚⱥa𝐚𝑎𝒂𝒶𝓪𝔞𝕒𝖆𝖺𝗮𝘢𝙖𝚊') ['a', 'à', 'á', 'â', 'ã', 'ä', 'ä', 'ā', 'ă', 'ą', 'ǎ', 'ǟ', 'ǡ', 'ǻ', 'ȁ', 'ȃ', 'ȧ', 'а', 'ӑ', 'ӓ', 'ᶏ', 'ḁ', 'ẚ', 'ⱥ', 'a', '𝐚', '𝑎', '𝒂', '𝒶', '𝓪', '𝔞', '𝕒', '𝖆', '𝖺', '𝗮', '𝘢', '𝙖', '𝚊'] >>> re.findall(r'\w', 'aàáâãääāăąǎǟǡǻȁȃȧаӑӓᶏḁẚⱥa𝐚𝑎𝒂𝒶𝓪𝔞𝕒𝖆𝖺𝗮𝘢𝙖𝚊', flags=re.ASCII) ['a'] >>> re.findall(r'(?a)\w', 'aàáâãääāăąǎǟǡǻȁȃȧаӑӓᶏḁẚⱥa𝐚𝑎𝒂𝒶𝓪𝔞𝕒𝖆𝖺𝗮𝘢𝙖𝚊') ['a'] >>> re.findall(r'\d', '01230123') ['0', '1', '2', '3', '0', '1', '2', '3'] >>> re.findall(r'\d', '01230123', flags=re.ASCII) ['0', '1', '2', '3'] >>> re.findall(r'(?a)\d', '01230123') ['0', '1', '2', '3'] >>> re.findall(r'\s', ' \t\n\r\f\v ') [' ', '\t', '\n', '\r', '\x0c', '\x0b', '\u3000'] >>> re.findall(r'\s', ' \t\n\r\f\v ', flags=re.ASCII) [' ', '\t', '\n', '\r', '\x0c', '\x0b'] >>> re.findall(r'(?a)\s', ' \t\n\r\f\v ') [' ', '\t', '\n', '\r', '\x0c', '\x0b']
整数関係の変更
long整数がなくなり、intに統一される。
また、int/intの値がintとならずfloatで返るようになる。小数部分を切り捨ててintで得るためにはint//int
とする。
list.sortメソッドに比較関数を使わなくなる
list.sortメソッドが引数cmp
を取らなくなる。代わりに比較に用いるキーを返す関数をキーワード引数key
で指定する。また、キーワード引数reverse
にTrue
を指定することで降順でソートされる。
イメージとしては、Perlでいうところのシュワルツ変換を行う感じだろうか。
set、dictを返す内包表記
>>> {i for i in range(0x61, 0x66)} {97, 98, 99, 100, 101} >>> {chr(i): i for i in range(0x61, 0x66)} {'a': 97, 'c': 99, 'b': 98, 'e': 101, 'd': 100}
まあ、いつか役に立つことがあるかもしれない。
モジュール名が整理される
urllib2
→urllib.request
、HTMLParser
→html.parser
、SimpleHTTPServer
→http.server
などなど、いろいろと名前が変わる。
これが一番やっかいな気がするけど、とりあえずurllib.request
さえ押さえておけばだいたいは大丈夫な気もする。
>>> import urllib.request as req >>> f = req.urlopen("http://www.example.com/") >>> f.readline() b'<!doctype html>\n' >>> f.close()