Python 3: 第 1 回 何が新しいのか

より良いコードのために整理された構文

Python 3 は Guido van Rossumによる強力な汎用プログラミング言語の最新バージョンです。Python 3 は 2.x シリーズとの後方互換性をなくし、その代わり構文に関するいくつかの問題を解決しています。この記事ではシリーズの第 1 回の記事として、Python 言語と後方互換性に影響する変更点について説明し、また新しい機能の例を紹介します。

Cesar Otero, Consultant, 自由职业者

author photo - cesar oteroCesar Otero は Java と Python に関するフリーのコンサルタントです。彼は電気工学の学位を持ち、副専攻は数学です。



2008年 12月 19日

Python バージョン 3 は、Python 3000 または Py3K (Microsoft® Windows® 2000 オペレーティング・システムをもじったニックネーム) として知られており、Guido van Rossumによる汎用プログラミング言語の最新バージョンです。コア言語に多くの改善が行われていますが、この新バージョンは 2.x シリーズとの後方互換性がありません。それ以外の変更点は長年期待されていたものであり、例えば次のようなものがあります。

  • 真の割り算 (例えば1/2 は .5 を返します)。
  • long 型と int 型は 1 つの型に統合され、末尾に付けられていた L はなくなりました。
  • TrueFalseNone がキーワードになりました。

この記事は Python 3 に関するシリーズの第 1 回として、新しい print() 関数、input()、入出力 (I/O) に関する変更、新しい byte データ型、そしてストリングとストリング・フォーマットへの変更について説明し、最後に組み込みの dict 型に対する変更について説明します。この記事は、既に Python をよく理解していて変更点に関心があるものの、PEP (Python Enhancement Proposal) の長いリストを 1 つ 1 つ調べるのは避けたいと思っているプログラマーを対象としています。(PEP へのリンクは下記の「参考文献」にあります)。

新しい print() 関数

Python 3 では、print "hello" と入力するのをやめ、print("hello") と入力するように指の訓練をやり直す必要があります。なぜなら Python 3 では print は関数であり、命令ではなくなったからです。もちろん私も、これが苦痛なことはわかっています。私が知っている Python プログラマーは全員、バージョン 3 をインストールした途端に「不正な構文 (incorrect syntax)」というエラーが表示され、苦痛の叫び声を上げています。私も 2 文字追加されたことが悩ましいことであるのは知っており、またこれによって後方互換性が失われることも知っています。しかしこうすることにはメリットがあるのです。

例えば標準出力 (stdout) をログにリダイレクトする場合を考えてみてください。下記の例は追加用の log.txt ファイルを開き、そのオブジェクトを fid に割り当てます。次に print>> を使ってストリングを fid ファイルにリダイレクトします。

>>>fid = open("log.txt", "a")
>>>print>>fid, "log text"

もう 1 つの例は標準的なエラー (sys.stderr) にリダイレクトする場合です。

>>>print>>sys.stderr, "an error occurred"

この 2 つの例はどちらも適切ですが、もっと良いソリューションがあります。新しい構文では、単純に print() 関数のキーワード引数 file に値を渡します。下記はその一例です

>>>fid = open("log.txt", "a")
>>>print("log.txt", file=fid)

このコードの方が構文はずっとスマートです。もう 1 つのメリットは、sep キーワード引数にストリングを渡すことで区切り文字を変更でき、また別のストリングを end キーワード引数に渡すことで終了文字を変更できることです。区切り文字を変更するためには以下のようにします。

>>>print("Foo", "Bar", sep="%")
>>>Foo%Bar

一般的に、新しい構文は以下のようになります。

print([object, ...][, sep=' '][, end='endline_character_here'][, file=redirect_to_here])

ここで、大括弧 ([]) の中のコードはオプションです。デフォルトで、print() を呼び出すこと自体によって改行文字 (\n) が追加されます。


raw_input() から input() へ

Python バージョン 2.x では、raw_input() は標準入力 (sys.stdin) からの入力を読み取り、最後に付いている改行文字が削除されたストリングを返します。次の例は raw_input() を使ってコマンド・プロンプトからストリングを取得し、その値を quest に割り当てます。

>>>quest = raw_input("What is your quest? ")
What is your quest? To seek the holy grail.
>>>quest
'To seek the holy grail.'

これとは対照的に、Python 2.x の input() 関数は有効な Python 式 (3+5 など) を想定します。

元々は、input()raw_input() のどちらも Python に組み込みの名前空間から完全に削除され、そのため何らかの入力機能を持つ import が必要だと言われていました。これは教育的に不健全に思えます。つまり単純に以下のように記述していたものが、

>>>quest = input("What is your quest?")

突然次のように変ってしまうのです。

>>>import sys
>>>print("What is your quest?")
>>>quest = sys.stdin.readline()

これは単なる入力のためには冗長すぎ、初心者には長々とした説明をしなければなりません。つまり、モジュールやインポートとは何か、またストリングの出力やドット演算子とは何かといったことを教えなければなりません。(これはあまりにも Java™ 言語に似すぎているように感じられます・・・) そこで Python 3 では、raw_input()input() と名前が変えられ、また標準入力からデータを取得するためにインポートは必要ありません。バージョン 2.x の input() 機能を維持する必要がある場合には、eval(input()) を使えば同じように動作します。


bytes オブジェクト

新しいデータ型であるバイト・リテラルは、bytes オブジェクトと同様に、バイナリー・データを保管するために使われます。bytes オブジェクトは 0 と 127 (つまり ASCII のみの文字) の間の整数の不変シーケンスです。実はこれは、バージョン 2.5 の bytearray オブジェクトの不変バージョンなのです。バイト・リテラルは、先頭に b が付いたストリングです (例えば b'byte literal' など)。バイト・リテラルを評価すると、新しい bytes オブジェクトが生成されます。新しい bytes オブジェクトを bytes() 関数を使って作成することもできます。bytes オブジェクトを作成するためには以下のようにします。

bytes([initializer[, encoding]])

例えば以下のようにすると、

>>>b = (b'\xc3\x9f\x65\x74\x61')
>>>print(b)
b'\xc3\x83\xc2\x9feta'

bytes オブジェクトが作成されますが、これでは冗長です。なぜなら bytes オブジェクトは単純にバイト・リテラルを割り当てるだけで作成できるからです。(私はこうすることもできることを示したかっただけであり、実際にこうするように推奨しているわけではありません。) もし iso-8859-1 エンコーディングを使いたい場合には、以下のようにすることもできます。

>>>b = bytes('\xc3\x9f\x65\x74\x61', 'iso-8859-1')
>>>print(b)
b'\xc3\x83\xc2\x9feta'

初期化子がストリングの場合にはエンコーディングを指定する必要があります。初期化子がバイト・リテラルの場合にはエンコーディング・タイプを指定する必要はありません。バイト・リテラルはストリングではないことを忘れないでください。しかしストリングと同様、以下のようにバイト・リテラルを連結することができます。

>>>b'hello' b' world'
b'hello world'

bytes() メソッドを使うと、バイナリー・データとエンコードされたテキストの両方を表現することができます。bytes から str に変換するためには、bytes オブジェクトをデコードする必要があります (これについては後ほど説明します)。バイナリー・データをデコードするためには以下のように decode() メソッドを使います。

>>>b'\xc3\x9f\x65\x74\x61'.decode()
'ßeta'

また、バイナリー・データをファイルから直接読み取ることもできます。例えば以下のコードは、

>>>data = open('dat.txt', 'rb').read() 
>>>print(data) # data is a string
>>># content of data.txt printed out here

ファイル・オブジェクトを読み取るためにバイナリー・モードでファイルを開き、ファイル全体を読み取ります。


ストリング

Python には、バージョン 2.x の unicode 型と似た動作をするストリング型 (str) があります。言い換えると、すべてのストリングは unicode ストリングです。また、(非ラテン語系テキストのユーザーには非常に便利なことに) ASCII ではない識別子が許可されるようになりました。以下はその一例です。

>>>césar = ["author", "consultant"]
>>>print(césar)
['author', 'consultant']

これまでのバージョンの Python では、repr() メソッドは 8 ビットのストリングを ASCII に変換しました。以下はその一例です。

>>>repr('é')
"'\\xc3\\xa9'"

Python 3 では、repr() メソッドは unicode ストリングを返します。

>>>repr('é')
"'é'"

これは先ほど触れたように、組み込みのストリング型です。

ストリング・オブジェクトとバイト・オブジェクトには互換性はありません。バイト・オブジェクトをストリングで表現したい場合には、そのオブジェクトの decode() メソッドを使います。逆に、ストリングのバイト・リテラルが必要な場合には、そのストリング・オブジェクトの encode() メソッドを使います。


ストリングのフォーマット設定方法の変更

多くの Python プログラマーは、ストリングのフォーマットを設定するための組み込みの % 演算子は制約が多すぎると感じていました。それは、以下の 2 つの理由からです。

  • % 演算子は二項演算子であり、引数は最大でも 2 つしか取れません。
  • ストリングのフォーマット設定用の引数以外に引数がある場合には、他のすべての引数をタプルまたはディクショナリーの中に詰め込まなければなりません。

このスタイルは多少柔軟性に欠けるため、Python 3 ではストリングのフォーマットを設定するために新しい方法を導入しています。(% 演算子も string.Template モジュールも、バージョン 3 には残っています。) Python 3 では、ストリング・オブジェクトは位置引数とキーワード引数を受け付ける format() メソッドを持ち、これらの引数は置換フィールドに渡されます。置換フィールドはストリングの中の中括弧 ({}) で表現されます。置換フィールドの中にある要素は単純にフィールドと呼ばれます。以下に示すのはその単純な一例です。

>>>"I love {0}, {1}, and {2}".format("eggs", "bacon", "sausage")
'I love eggs, bacon, and sausage'

フィールド {0}{1}{2} には format() メソッドの位置パラメーターである eggsbaconsausage が渡されます。以下の例は format() を使ってキーワード引数を渡すことでフォーマットを設定する方法を示しています。

>>>"I love {a}, {b}, and {c}".format(a="eggs", b="bacon", c="sausage")
'I love eggs, bacon, and sausage'

以下に示すのは位置パラメーターとキーワード引数とを組み合わせた別の例です。

>>>"I love {0}, {1}, and {param}".format("eggs", "bacon", param="sausage")
'I love eggs, bacon, and sausage'

キーワードではない引数をキーワード引数の後に置くのは構文エラーだということを忘れないでください。中括弧をエスケープするためには、以下のように中括弧を二重にします。

>>>"{{0}}".format("can't see me")
'{0}'

この場合、位置パラメーター can't see me は出力されませんが、これは出力用のフィールドがないからです。こうしてもエラーにはならないことに注意してください。

format() という新しい組み込み関数は 1 つの値のフォーマットを設定します。以下はその一例です。

>>>print(format(10.0, "7.3g"))
       10

つまり g は汎用 (general) フォーマットを表し、固定長の数字を出力します。ドットの前にある最初の数字は最小桁数を指定し、ドットの後にある数字は精度を指定します。この記事ではフォーマット指定子に関する完全な構文の説明は省略しますが、詳しい情報へのリンクは「参考文献」セクションを参照してください。


組み込みの dict 型に対する変更

3.0 での、もう 1 つの大きな変更は、dict.iterkeys() メソッドと dict.itervalues() メソッド、そして dict.iteritems() メソッドがディクショナリーから削除されたことです。これらの代わりに .keys().values().items() を使いますが、これらは (キーまたは値のコピーであるリストの代わりに) 軽量で set のようなコンテナー・オブジェクトを返すように変更されています。これによるメリットは、キーや項目に対して set 操作を行うことができ、キーや項目をコピーする必要がないことです。以下はその一例です。

>>>d = {1:"dead", 2:"parrot"}
>>>print(d.items())
<built-in method items of dict object at 0xb7c2468c>

注: Python では、set は一意の要素を順不同で集めたものです。

ここでは 2 つのキーと値を持つディクショナリーを作成し、次に d.items() の値を出力しています。そうすると値のリストではなくオブジェクトが返されます。ある要素のメンバーかどうかのテストは set オブジェクトの場合と同じように行うことができます。

>>>1 in d # test for membership
True

以下に示すのは dict_values オブジェクトの項目に対して繰り返し処理を行う例です。

>>>for values in d.items():
...     print(values) 
...
dead
parrot

しかし、どうしても値のリストが必要な場合には、返された dict オブジェクトをキャストすることでいつでもリストが得られます。以下はその一例です。

>>>keys = list(d.keys())
>>>print(keys)
[1,2]

新しい I/O

メタクラス

ウィキペディアによれば、「メタクラスとは、インスタンスがクラスであるクラス」です。この概念の詳細はこのシリーズの第 2 回で説明します。

I/O のための新しいメカニズムを説明する前に、ABC (abstract base class: 抽象基底クラス) について復習する必要があります。ABC の扱い方の詳細についてはこのシリーズの第 2 回に説明します。

ABC はインスタンス化できないクラスです。ABC を使うためには、ABC を継承し、継承したサブクラスの抽象メソッドをオーバーライドする必要があります。あるメソッドの前に修飾子 @abstractmethod があると、そのメソッドは抽象メソッドです。新しい ABC フレームワークには、抽象プロパティーを定義するための @abstractproperty 修飾子も用意されています。この新しいフレームワークを利用するためには、標準ライブラリー・モジュール abc をインポートします。リスト 1 は簡単な例を示しています。

リスト 1. 単純な抽象基底クラス
from abc import ABCMeta

class SimpleAbstractClass(metaclass=ABCMeta):
    pass

SimpleAbstractClass.register(list)

assert isinstance([], SimpleAbstractClass)

register() メソッドの呼び出しはクラスを引数に取り、ABC を、登録されたクラスのサブクラスにします。これを確認するために最後の行で assert 命令を呼び出します。リスト 2 は修飾子を使う別の例です。

リスト 2. 修飾子を使って実装された抽象基底クラス
from abc import ABCMeta, abstractmethod

class abstract(metaclass=ABCMeta):
    @abstractmethod
    def absMeth(self):
        pass
 
class A(abstract):
    # must implement abstract method
    def absMeth(self):
        return 0

これで ABC について理解できたので、新しい I/O システムの説明を続けましょう。これまでの Python のリリースには、風変わりでも重要な関数、例えばストリームのようなオブジェクトに対する seek() などがありませんでした。ストリームのようなオブジェクトというのは read() メソッドと write() メソッドを持つファイルのようなオブジェクトであり、例えばソケットやファイルなどです。Python 3 にはストリームのようなオブジェクトに対して複数の I/O レイヤー (そのままの I/O レイヤー、バッファー付き I/O レイヤー、そしてテキスト I/O レイヤー) があり、それぞれのレイヤーには独自の ABC が実装と共に定義されています。

io.open(fileName)) を呼び出すこともできますが、ストリームを開くにはやはり組み込みの open(fileName) 関数を使います。そうすることで、バッファリングされたテキスト・ファイルが返されます。つまり read()readline() によってストリングが返されるのです。(Python 3 ではすべてのストリングが unicode であることを忘れないでください。) また、バッファリングされたバイナリー・ファイルを open(fileName, 'b') という形式を使って開くこともできます。この場合、read() はバイトを返しますが、readline() を使うことはできません。

組み込みの open() 関数を使うには以下のようにします。

open(file,mode="r",buffering=None,encoding=None,errors=None,newline=None,closefd=True)

mode に指定できるのは以下のモードです。

  • r: 開いて読み取るモード
  • w: 開いて書き込むモード
  • a: 開いて追加するモード
  • b: バイナリー・モード
  • t: テキスト・モード
  • +: ディスク・ファイルを開いて更新するモード
  • U: 汎用の改行モード

デフォルトのモードは rt、つまり「開いてテキスト・モードで読み取る」です。

buffering というキーワード引数を付けると、バッファリング方法を決定するための下記の 3 つの整数の 1 つを受け付けることになります。

  • 0: バッファリングをオフにする
  • 1: 行バッファリング
  • > 1: フル・バッファリング (デフォルト)

デフォルトのエンコーディングはプラットフォームに依存します。ファイルを閉じるための記述子、つまり closefd は、True または False になります。False の場合、ファイルが閉じられた後にファイル記述子が保持されます。ファイル名を指定しても動作しない場合には、closefd が True に設定されているはずです。

open() によって返されるオブジェクトは設定されたモードに依存します。表 1 は戻り型を示しています。

表 1. 開くモードによる戻り型の違い
モード返されるオブジェクト
テキスト・モードTextIOWrapper
バイナリーBufferedReader
バイナリーで書き込みBufferedWriter
バイナリーで追加BufferedWriter
読み書きモードBufferedRandom

注: テキスト・モードは wrwtrt などです。

リスト 3 の例はバッファリングされたバイナリー・ストリームを開いて読み取ります。

リスト 3. バッファリングされたバイナリー・ストリームを開いて読み取る
>>>import io
>>>f = io.open("hashlib.pyo", "rb")  # open for reading in binary mode
>>>f                                 # f is a BufferedReader object 
<io.BufferedReader object at 0xb7c2534c>
>>>f.close()                         # close stream

BufferedReader オブジェクトを使うと、いくつかの便利なメソッドにアクセスすることができます (一部を挙げるだけでも、isattypeekrawreadintoreadlinereadlinesseekseekabletellwritablewritewritelines などがあります)。完全な一覧を見るためには BufferedReader オブジェクトに対して dir() を実行します。


まとめ

Python コミュニティーがバージョン 3 を受け入れるかどうかは誰にもわかりません。後方互換性がないということは、2 つの異なるバージョンを並行してサポートするということです。一部のプロジェクト開発者は、たとえバージョン 2 から 3 へのコンバーターがあったとしても彼らのプロジェクトを移行したくないと思うかもしれません。個人的には、私は Python バージョン 2 から 3 への移行は基本的に、いくつかのことを学び直すことだということに気付きました。もちろんこの移行は、例えば Python から Java 言語や Perl 言語に移行するほどの劇的な変更ではありませんでした。多くの変更は長年待ち望まれていたものです (例えば真の割り算や dict に対する変更など)。print() を実行する方が Java での System.out.println() よりも遥かに容易であるため、学習期間は短くてすみ、しかも多くのものを得ることができます。

ブロゴスフィアのエントリーを読んでみると、多くの Python 主義者達は、いくつかの変更 (例えば後方互換性をなくしたこと) は約束違反だと考えているようです。ラムダは元々削除される予定でしたが、残っており、しかも元の形式のままです。変更されていない内容の完全な一覧は Python のコア開発のサイトを参照してください。冒険心に満ちた人ならば、PEP を調べれば詳細な情報を見つけることができるはずです。

このシリーズの次回の記事では、より高度な話題、例えばメタクラス構文、抽象基底クラス (ABC)、修飾子、整数リテラルのサポート、基底型、例外などについて説明する予定です。

参考文献

学ぶために

製品や技術を入手するために

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Linux, Open source
ArticleID=366653
ArticleTitle=Python 3: 第 1 回 何が新しいのか
publish-date=12192008