このBlogは移転しました。今後は aish.dev を御覧ください。

Python文法詳解(一刷) 正誤表

以下の正誤表は、一刷のものです。二刷の正誤表は Python文法詳解(二刷) 正誤表 - atsuoishimoto's diary を参照してください。

第1章

P.1

次のようなというカテゴリーになるでしょう

次のようなカテゴリーになるでしょう

第2章

P.18

>>> 10 // 3
3.0
>>> 10.0 // 3.0
3.0

P.21

>>> S[1:4]
'bcd
>>> S[1:4]
'bcd'

第3章

p.48

>>> a = 1
>>> 100 and x == 1 or 200   # x if y else z と同じ式
>>> x = 1
>>> x == 1 and 100 or 200   # x if y else z と同じ式
100

p.52

base - 文字列を整数に変換する際の基数を指定し、省略時は10進数として変換します 0 の場合は、Python の整数リテラルとして変換します。

base - 文字列を整数に変換する際の基数を指定し、省略時は10進数として変換します。0 の場合は、Pythonの整数リテラルとして変換します。

p.53

>> int('0b100', 0) # 2進数
100
>> int('0b100', 0) # 2進数
4

第4章

P.72

>>> L[8:2:-1]    # 9番目から3番目まで取得
['8', '7', '6', '5', '4', '3']
>>> L[10:1:-3]   # 10番目から1番目まで、後ろから2つおきに取得
['9', '6', '3']
>>> L[8:2:-1]    # 9番目から4番目まで取得
['8', '7', '6', '5', '4', '3']
>>> L[10:1:-3]   # 10番目から3番目まで、後ろから2つおきに取得
['9', '6', '3']

P.74

>>> slice(1, 10)    # [1:10] と同じ
slice(None, 10, 3)
>>> slice(1, 10)    # [1:10] と同じ
slice(1, 10, None)

P.75

>>> L[3:7] = ['a', 'b', 'c', 'd']   # 4番目から7番目までの要素を置き換え
>>> L
[0, 1, 2, 'a', 'b', 'c', 'd', 7, 8, 9]
>>> L[1:4] = ['a', 'b', 'c', 'd']   # 2番目から4番目までの要素を置き換え
>>> L
[0, 'a', 'b', 'c', 'd', 4, 5, 6, 7, 8, 9]

P.88

start、endを指定したときは、スライス演算と同じ規則で指定する範囲から、subを検索します。

start、endを指定したときは、スライス演算と同じ規則で指定する範囲から、valueを検索します。

P.92

c = a;
b = a;
a = c;
c = b;
b = a;
a = c;

P.96

>>> hello = '今日は'
>>> hello.decode('utf-8')
b'\xe4\xbb\x8a\xe6\x97\xa5\xe3\x81\xaf'
>>> hello = '今日は'
>>> hello.encode('utf-8')
b'\xe4\xbb\x8a\xe6\x97\xa5\xe3\x81\xaf'

P.97

>>> s = 'とても
... 長くて
... 一行に収まらない
... 文字列'
>>> s = 'とても\
... 長くて\
... 一行に収まらない\
... 文字列'

P.112

>>> 'spam'.isalnum()
True
>>> 'スパムハム卵'.isalnum()    # 日本語の文字もTrue
True
>>> 'spam'.isalpha()
True
>>> 'スパムハム卵'.isalpha()    # 日本語の文字もTrue
True

P.121

>>> 'spam ham egg sausage'.split(maxsplit=2)   # 最大2回分割
['spam, 'ham', 'egg sausage']
>>> 'spam ham egg sausage'.split(maxsplit=2)   # 最大2回分割
['spam', 'ham', 'egg sausage']

P.129

b'\nspam\nham\negg\n\\t'
b'\nspam\nham\negg\n\t'

P.132

>>> bytes.fromhex('73 70 61 6d'')
>>> bytes.fromhex('73 70 61 6d')

P.146

chars を省略した場合、またはNoneを指定した場合は、全ての空白文字を削除します。

bytes を省略した場合、またはNoneを指定した場合は、全ての空白文字を削除します。

P.147

文字列が + か - の符号ではじまる場合、符号をバイト列の先頭に移動します。

バイト列が + か - の符号ではじまる場合、符号をバイト列の先頭に移動します。

P.152

>>> d['spam'] = 'egg' # キー 'ham' の値を `egg` に上書き
>>> d['spam'] = 'egg' # キー 'spam' の値を `egg` に上書き

P.156

逆にnot in 演算子は、左項のキーが、右項の辞書に含まれない場合に、True を返します。not in 演算子は、逆に辞書に左項のオブジェクトと同じ値の要素が含まれない場合に、True を返します。

逆にnot in演算子は、左項のキーが、右項の辞書に含まれない場合に、Trueを返します。

P.158

また、イテレータによるキーの列挙は、列挙中に辞書が変更されると無効になります。

また、イテレータによるキーの列挙は、列挙中に辞書の要素数が変更されると無効になります。

P.159

他の集合やイテラブルオブジェクトと、集合演算も行えます。

>>> d = {'spam':1, 'ham':2, 'egg':3, 'bacon':4}
>>> view = d.keys()
>>> view - ['spam', 'bacon']
{'spam', 'bacon'}

他の集合やイテラブルオブジェクトと、集合演算も行えます。

>>> d = {'spam':1, 'ham':2, 'egg':3, 'bacon':4}
>>> view = d.keys()
>>> view - ['spam', 'bacon']
{'ham', 'egg'}

P.164

集合オブジェクトが返すキーの順番は一定ではなく。

集合オブジェクトが返すキーの順番は一定ではなく、

P.164

要素の列挙は、列挙中に集合が変更されると無効になります。

要素の列挙は、列挙中に集合の要素数が変更されると無効になります。

P.169

>>> s.discad(1)
>>> s.discard(1)

第5章

P.180

start - インデックス値の初期値を指定します。省略時は 1 となります。

step - 数値の増分を指定します。省略時は 1 となります。

start - インデックス値の初期値を指定します。省略時は 0 となります。

P.184

キーボード割り込みを書けられた時に、

キーボード割り込みをかけられた時に、

第6章

P.193

>>> def spam(ham, egg, *args):
...     print('ham={}, egg={}, args={}'.format(ham, egg, args))}
>>> def spam(ham, egg, *args):
...     print('ham={}, egg={}, args={}'.format(ham, egg, args))

P.204

from ..ham.egg import bacon # 親パッケージのham.egg.spam モジュールをインポート
from ..ham.egg import bacon # 親パッケージのham.egg.bacon モジュールをインポート

P.206

$ cat spamp.zip >>spam
$ cat spam.zip >>spam

P.210

from spam import func_spam
import spam
func_ham = spam.func_spam

P.210

# egg モジュールは、ham モジュールをインポートして、func_spam を呼び出す
import ham

ham.func_spam()
# egg モジュールは、ham モジュールをインポートして、func_spam を呼び出す
import ham
ham.func_ham()

P.210

egg モジュールでは、ham モジュールを経由して、ham.func_spam() 関数を呼び出しています。

egg モジュールでは、ham モジュールを経由して、spam.func_spam() 関数を呼び出しています。

P.210

このように、呼び出し方にかかわらず、関数を作成したモジュールが、常にその関数のグローバル名前空間として検索されです。

このように、呼び出し方にかかわらず、関数を作成したモジュールが、常にその関数のグローバル名前空間として検索されます。

P.220

... super().ham() # 基底型の ham() を呼び出す
... super().spam() # 基底型の spam() を呼び出す

P.227

したがって、インスタンスから属性HAMを取得すると、インスタンス名前空間の検索は失敗しますが、失敗しますが、自動的にクラスの名前空間を次に検索し、

したがって、インスタンスから属性HAMを取得すると、インスタンス名前空間の検索は失敗しますが、自動的にクラスの名前空間を次に検索し、

P.229

def ham(self, arg):
    print(arg)
def egg(self, arg):
    print(arg)

P.229

Spam = type('Spam', (), {'ham':method, 'ham':100})
Spam = type('Spam', (), {'ham':100, 'egg':method})

P.230

type.__prepare__(metacls , name , bases , \*\*kwargs)

パラメタ

  metacls - メタクラス名を指定します。
  name - クラス名を指定します。
type.__prepare__(name , bases , \*\*kwargs)

パラメタ

  name - クラス名を指定します。

第7章

P.250

  • 誤 (7.1.6.6項)

raw -読み込みを行う、バッファなしストリームオブジェクトを指定します。

raw -書き込みを行う、バッファなしストリームオブジェクトを指定します。

P.251

raw -読み込みを行う、バッファなしストリームオブジェクトを指定します。

raw -読み書きを行う、バッファなしストリームオブジェクトを指定します。

P.252

line_buffering - True を指定する行バッファリングとなり、改行文字を出力した後に出力バッファをフラッシュします

line_buffering - True を指定すると、行バッファリングとなり、改行文字を出力した後に出力バッファをフラッシュします

P.262

処理を処理を再開します。

処理を再開します。

P.266

>>> list(x+y for x in 'abc'
... for y in '123' if y < '3'} # for のネストと if
>>> list(x+y for x in 'abc'
... for y in '123' if y < '3') # for のネストと if

P.267

print('Spam: {0.spam}, Ham: {0.ham}'.format(self.spam, self.ham))
print('Spam: {0.spam}, Ham: {0.ham}'.format(self))

P.267

... print('Hello {!'.format(', '.join(names))}
... print('Hello {}!'.format(', '.join(names))}

P.271

メソッド呼び出しので第一引数として、インスタンスではなく、常にクラスオブジェクトが渡されるようになります。

メソッド呼び出しの第一引数として、インスタンスではなく、常にクラスオブジェクトが渡されるようになります。

P.273

>>> spam.x
0
>>> spam.__dict__['ham'] = 999  # インスタンスの属性値を変更
>>> spam.x                      # プロパティは影響を受けない
0
>>> spam.ham
0
>>> spam.__dict__['ham'] = 999  # インスタンスの属性値を変更
>>> spam.ham                    # プロパティは影響を受けない
0

P.274

ダック・タイピング(Dock typing)

ダック・タイピング(Duck typing)

P.288

__getattribute__(attr)

__getattribute__(self, attr)

第8章

P.294

object - 参照カウントをオブジェクトを指定します。

object - 参照カウントを取得するオブジェクトを指定します。

P.299

Pythonは動的なプログラミング言語なので、関数を呼び出したり、オブジェクトを属性を参照したとき、

Pythonは動的なプログラミング言語なので、関数を呼び出したり、オブジェクトの属性を参照したとき、

P.299

そのように、オブジェクトを調査することイントロスペクションと呼びます。

そのように、オブジェクトを調査することをイントロスペクションと呼びます。

P.301

>>> type(unknown_obj).__module__.__file__
'/usr/local/lib/python3.4/site-packages/unknown.py
>>> import unknown_module
>>> unknown_module.__file__
'/usr/local/lib/python3.4/site-packages/unknown_module.py

P.302

行番号 バイト ニーモニック 引数 引数が示す値
       コード
       の位置
行番号 バイト 命令コード 引数 引数が示す値
       コード
       の位置

謝辞

本正誤表の作成にあたっては、下記の皆様をはじめ、多くの方々のご協力を頂きました。心よりお礼申し上げます。

(順不同)

tse 0.0.9

tse(Text Stream Editor) 0.0.9 をリリースした。このリリースでは、--begin オプションと、--end オブションを複数指定できるように修正した。

これまでだと、--begin複数行の値を指定するとき、

$ tse --begin 'print(1)' --begin 'print(2)'

複数--begin を記述する必要があったが、0.0.9 以降では

tse --begin 'print(1)' 'print(2)'

のように、--begin オプションに複数行を指定できるようになった。

この修正により、

tse --begin 'print(1)' *.txt

のように、入力ファイル名を --begin/--end の直後に指定していると、ファイル名がPythonスクリプトとして認識され、エラーとなってしまう。

このような場合は、次のようにスクリプトの終わりを -- で明示的に指定する必要がある。

tse --begin 'print(1)' −− *.txt

PyCon JP 2015 発表資料

PyCon JP 2015 で、tse の発表をさせていただき、ありがとうございました。思ったよりたくさんの、ワンライナーを愛する善男善女に聞いていただきました。

sed/awkが必修科目だった昔とは違って、今では知らない人も結構多いんじゃないかと思ってたけど、このセッションの参加者では、7割ぐらいの方がawkの利用経験ありということだった。awkスタイルのテキスト処理への関心も高いようだった。

「tseよりもsed/awk使い続けたほうが楽だなあ」という声もあったようだけど、それはもちろんそうで、sed/awkのような専門ツールを使ったほうが効率が良いケースはたくさんある。しかし、awkを知らない人や、awkでは難しいタスクを実行するときには、tseは非常に優れた選択肢になり得ると思う。機会があったら、ぜひとも一度お試しいただきたい。

tse 0.0.5リリース

久しぶりに tse をリリースした。遅ればせながら Python3で動くように修正し、いくつかオブションを追加した。

tse は sedawk のように Pythonコマンドラインから実行するためのツールで、詳しくは 以前書いた解説 を参照していただきたい。

今回のリリースでは、実行する Python スクリプトを、 {{}} を使ってインデントを記述できるようにしてみた。

tse -p '' -a 'if L1:{{for c in L2:{{print(c)}}else:{{print(L3)}}}}else:{{print(L4)}}'

のようなスクリプトは、

if L1:
    for c in L2:
        print(c)
    else:
        print(L3)
else:
    print(L4)

に変換される。tse のコマンドラインパーサは Pythonの構文を理解しており、文字列定数やコメント中に {{}} があっても無視するようになっている。

また、--inplace 引数に、ファイルの拡張子を指定すると、出力は標準出力ではなく、入力ファイルを上書きするようになった。元の入力ファイルは、--inplace 引数で指定した拡張子をつけたファイル名に保存される。

Python3対応もできたので、ずいぶん出番が増えてきた。最近では以下の様な使い方をしていた。

  • 全角・半角文字列を正規化

    {.sourceCode .sh} $ echo '123アイウエオ' | tse -ms unicodedata -s '.*' 'print(normalize("NFKC", L))'

  • スペース区切りのテキストからcsvに変換

    {.sourceCode .sh} $ tse -m csv -b 'o=csv.writer(sys.stdout)' -s '.*' 'o.writerow((L1,L2,L3))' < ~/C.txt

  • gitのユーザ名をコミット数順に表示

    {.sourceCode .sh} $ git log|tse -ms collections -b 'c=Counter()' -s '^Author: (.*)' 'c[S1] +=1' -e 'for a, n in c.items():print(n, a)'|sort -g

  • ファイルの文字コード変換

    {.sourceCode .sh} $ tse --inplace='.bak' -ie euc-jp -oe shiftjis -s '.*' 'print(L)' -- a.txt

  • 特定の文字列を含むプロセスを kill

    {.sourceCode .sh} $ ps -a|tse -s python 'print(L.split()[0])'|xargs sudo kill

python.jp改善計画

先日、CROSS2015 というイベントで、日本のPythonコミュニティ全般についてのパネルディスカッションに参加させていただいた。

90分という長丁場で、途中でトイレ行きたくなったらどうしようなどと余計な心配をしていたが、とくにダレることもなく緊張感をもって最後まで楽しく過ごせた。会場にお運びいただいた皆様と、パネラーの皆様にお礼を申し上げたい。

この辺の話をするにあたって、python.jp の今後について、いろいろと考えることがあった。大した話ではないが、今後のpython.jpの予定についてちょっと書いておきたい。

Job Boardの作成

Pythonを本格的に採用している企業などでも、Pythonプログラマの求人というのはなかなか難しかったりするので、少しでもその辺のサポートをしたい。

掲載は無料。掲載期間は6ヶ月で、6ヶ月たったら再掲載の申し込みがない限りは削除。 人材採用企業などからの掲載はお断り。ってな感じでやろうかなと。

メーリングリストの停止

現在、Python.jpでは Mailman を使ったメーリングリストを運用している。しかし、自前のメーリングリストサービスを使っていても、特にこれといった付加価値を提供できているわけではない。また、運用負荷が高い割には、どのメーリングリストもあまり利用されていない。

ということで、現在のメーリングリストは、近いうちにGoogle groupに移行しようと考えている。メーリングリストのアーカイブはこのまま残すが、新規の投稿は停止する。

移行手順とスケジュールは、決まり次第各メーリングリストに通知する予定。

また、メーリングリストの代わりに、だれでも簡単に参加できて使いやすいChat系のサービスがあれば、使ってみたいと思っている。<https://gitter.im> あたりはgithubのアカウントがあれば無料で使えて、MarkdownでPythonスクリプトの構文ハイライトもできるし良いかなとおもうけど、Slackあたりと比べうと機能的にはちょっとさびしいか?

ドキュメント翻訳プロジェクト

さいきんあまり参加できていないが、私がコミュニティ活動として一番大事に考えているのは、Pythonドキュメント翻訳プロジェクト だ。

Python.jpを使って、もうちょっとドキュメント翻訳プロジェクトの活動や成果を可視化できるような仕組みを考えたいと思っている。

「PyJUG」の終わり

利用者にはあまり関係のない話だが、これまで、「python.jpは 日本Pythonユーザ会(Python Japan User's Group:PyJUG) が運営しています」という建前でやってきた。Python.jpは日本を代表するPythonコミュニティであり、またPython.jpを私物化するつもりはない、という意思表示のためだ。

しかし、「PyJUG」はあまり実体のない、名前だけの存在で、現在ではメンバーも定まっていない。むかしpython.jpを立ち上げた頃には10人ぐらい集まってメーリングリストでわいわい話しあったりしたが、特に目立った活動をしたりイベントを実施したりということも無い。

この実体のない「PyJUG」という名前を出しているばかりに、「PyJUGってなんですか。なんか怖い人達のあつまりなんでしょ」というような怪しい雰囲気を醸し出してしまうことが多い。

ということで、混乱を招くだけの「PyJUG」はもうやめようと思っている。今どき、Webサイトを運営するのにユーザ会は必要ないだろう。githubにサイトのソースを公開し、希望者に更新権限を付与するだけで十分だ。

Python2とその時代

宣伝が続いて恐縮だが、オライリージャパンよりPythonの解説書を上梓した。昔から、Python内部の仕組みも解説したPython解説書を書きたいと思っていて、ようやく実現した感じだ。

しかし、本書の執筆は、昔、構想を立てていたときに思っていたほどは楽しくはなかった。Python2ではなく、Python3.3以降を対象に書いてしまったからだ。Python3の型システムは綺麗に整理されてしまったし、メタクラスも扱いやすくなった。メモリアロケータは改善され、ガベージコレクションの注意点も大幅に減った。Unicodeの暗黙の変換も無くなった。私が書こうと思っていた知識の多くは、Python3では無用の長物と化した。

本書をPython2向けに書いていたら、今よりずっと多くのページ数を費やしただろう。Python3は、Python2よりも落とし穴が少なく、学習も容易なプログラミング言語だ。

そんな優れたPython3だが、「普及してないじゃないか」という声が上がっているようだ。以下、その件に関する感想を書いておく。

Python3は普及してない?

まあ、Python2に比べてば、普及してない。当然だ。Pythonは20年以上の歴史を持ち、熱心に開発され、幅広く利用されてきた。Pythonは、プログラミング史上、もっとも大きな成功を収めたプログラミング言語の一つだ。2008年末にリリースされた、若造プログラミング言語のPython3に太刀打ちできるはずがない。

一度作成されたプログラムが、大した理由もなしに別の言語や環境に対応することはありえない。時間が経てば、自然とPython2->3に移行されるというものではない。

現在、Python3がリリースされてから、5年ちょっと。Pythonが最初にリリースされてから5年経った頃というと、Python1.4とか1.5とかの頃だろうか。あの頃のPythonコミュニティも、まだまだ小さかった。今のPython3ユーザより、当時の全Pythonユーザの方が少ないのではないだろうか。comp.lang.pythonに流れるメールを全部読んでも大した量ではなかったし、開発者の数もわずかだった。リリースから10年以上たった2003年、私はPyCon USに遊びにいったが、その頃でも、参加者の数はたしか400人程度だった。

プログラミング言語の普及というのは、時間がかかるものだ。2000年代後半にも、いろいろなプログラミング言語が登場してきた。その中で、Python3以上のユーザを獲得した言語はどれだけあるだろうか?

issubclass(Python3, Python)?

同じ「Python」の名を冠してはいるが、Python3は、やはりPython2ではない。異なるプログラミング言語だ。Pythonはこれまで大きな成功を収め、巨大なエコシステムを築いてきた。沢山のモジュールが開発され、ほとんどのLinuxにはデフォルトでインストールされている。利用者に見えないところで、いろんなPythonアプリケーションが動いている。Windowsのシステムディレクトリには、いつの間にかPythonxx.dllがインストールされている。

Python3はPython2と互換性がないので、このエコシステムをそのまま利用することはできない。ほとんどのPython2モジュールは、Python3でも使えるようにするには修正が必要で、Python2と3両方で使えるようにするのは大変な作業だ。Python2専用か、Python3専用のどちらか決めてしまったほうが、はるかに簡単になる。

そういう意味で、Python2コミュニティと、Python3コミュニティは「分断」される。これから開発される、Python3用のモジュールやアプリケーションは、Python2コミュニティでは利用できないだろう。また、Python2だけを利用するユーザは、Python2専用のモジュールを開発するだろう。後知恵だが、Python開発者たちはPython2/3共用アプリの開発コストを低く見積もりすぎていた。Python3は、Python2という巨大なライバルを相手に、自分の陣地を獲得しなければならない。これには、まだ時間が必要だろう。

Python2の落日

Python2は、未だに隆盛をきわめているが、その未来は明るくはない。御存知の通り、Python2の開発は終了しており、今後、Python2に機能追加などが行われることはない。結果として、Python開発チームは、とてもポピュラーなプログラミング言語を「殺し」、かわりにPython3という、新しいプログラミング言語を開発することを選んだ。

Python3なんかいらない、Unicodeなんか使わない、というユーザのいらだちは理解できる。Python2は十分現役で、そのまま使い続けるのに何の問題もないのに、お前のアプリ・モジュールは時代遅れになるから書き直せ、と言われてしまう。Pythonを単にツールに利用しているだけの人たちにとっては、迷惑なことだろう。

とはいえ、Python2は、非常に広く普及したので、本当に消えてなくなるまでには長い時間がかかるだろう。現在、bash などのシェルがこれだけ普及していても、移植性があるスクリプトを書くときには /bin/sh 用のスクリプトだけで書いたりするが、そんな感じで、/bin/python に Pyton2 がインストールされ続けるのかもしれない。そうなると、Python2の利用者が目に見えて減ることは無いのかもしれない。

Python3のエコシステム

今、Python2を使っているなら、焦る必要はない。いまあるPython2とライブラリで目的を果たせるなら、当面、Python2を使い続けるのは悪いことでもなんでもない。Python2の方が好きなら、全力でPython2をサポートするのもよいだろう。

しかし、分断されたPython2/3を統合するには、Python3のエコシステムを整備するしかない。Python3を強化し、Python2からの移行コストより、メリットが大きいことが明らかになるようにしなければならない。そのためには、ユーザがPython3を使い、改善に協力することが必要だ。

(ちなみに、個人的には、PEP 3003 Python Language Moratorium は間違いだったと思ってる。追いかけて欲しければ、全力で逃げるべきなのだ)

Python2か、Python3かで迷うなら、Python2を使うのも良いだろう。その昔は、Perlを使うか、Pythonを使うかで迷ったものだ。多少のリスクは覚悟した上で、Pythonを使っていた人たちがいて、その積み重ねがPythonの今日を作り上げた。同じ積み重ねが、Python3でもまた繰り返されると信じて、私は今日もPython3を起動している。