プログラミング FAQ¶
目次
- プログラミング FAQ
- 一般的な質問
- コア言語
- なぜ変数に値があるのに UnboundLocalError が出るのですか?
- Python のローカルとグローバル変数のルールは何ですか?
- ループの中で異なる値で定義されたラムダ式が、同じ値を返すのはなぜですか?
- グローバル変数をモジュール間で共有するにはどうしたらいいですか?
- モジュールで import を使う際の「ベストプラクティス」は何ですか?
- なぜオブジェクト間でデフォルト値が共有されるのですか?
- オプションパラメータやキーワードパラメータを関数から関数へ渡すにはどうしたらいいですか?
- 実引数と仮引数の違いは何ですか?
- なぜ list 'y' を変更すると list 'x' も変更されるのですか?
- パラメータを出力する関数 (参照渡し) はどのように書きますか?
- Python で高次関数はどのようにつくりますか?
- Python のオブジェクトはどのようにコピーしますか?
- オブジェクトのメソッドや属性はどのように見つけますか?
- コードはどのようにオブジェクトの名前を見つけるのですか?
- カンマ演算子はなぜ優先されるのですか?
- C の "?:" 三項演算子と等価なものはありますか?
- Python で解し難いワンライナーを書くことはできますか?
- 数と文字列
- 十六進数や八進数を指定するにはどうしたらいいですか?
- なぜ -22 // 10 は -3 を返すのですか?
- 文字列を数に変換するにはどうしたらいいですか?
- 数を文字列に変換するにはどうしたらいいですか?
- 文字列をインプレースに変更するにはどうしたらいいですか?
- 関数やメソッドを呼ぶのに文字列を使うにはどうしたらいいですか?
- 文字列から後端の改行を取り除く Perl の chomp() に相当するものはありますか?
- scanf() や sscanf() と同等なものはありますか?
- 'UnicodeError: ASCII [decoding,encoding] error: ordinal not in range(128)' とはどういう意味ですか?
- シーケンス(タプル/リスト)
- 辞書
- オブジェクト
- クラスとは何ですか?
- メソッドとは何ですか?
- self とは何ですか?
- あるオブジェクトが、与えられたクラスやそのサブクラスのインスタンスであるかを調べるにはどうしますか?
- 委譲とは何ですか?
- 基底クラスで定義されたメソッドを、そのクラスをオーバーライドした派生クラスから呼び出すにはどうしますか?
- 基底クラスの名前を変えやすいコードを書くにはどうしますか?
- 静的なクラスデータや静的なクラスメソッドを作るにはどうしますか?
- Python でコンストラクタ(やメソッド)をオーバーロードするにはどうしたらいいですか?
- __spam を使おうとしたら _SomeClassName__spam からエラーがでました。
- クラスに __del__ メソッドを定義しているのですが、オブジェクトを削除したときに呼ばれません。
- 与えられたクラスのすべてのインスタンスのリストを得るにはどうしますか?
- なぜ
id()
の結果は一意でないように見えるのですか?
- モジュール
一般的な質問¶
ブレークポイントやシングルステップ実行などを備えたソースコードレベルデバッガはありますか?¶
はい。
pdb モジュールは簡素にして十分な Python のコンソールモードデバッガです。これは Python の標準ライブラリに含まれているもので、 ライブラリリファレンスマニュアルにドキュメントがあります
。 pdb のコードを手本にして自分用のデバッガを書くこともできます。
Python に同梱されている統合開発環境の IDLE は通常の Python の配布形態の一部 (普通は Tools/scripts/idle から利用可能) であり、グラフィカルなデバッガを含んでいます。
PythonWin は、pdb をベースとした GUI デバッガを含む Python IDE です。Pythonwin デバッガは、ブレークポイントの色付けや非 Pythonwin プログラムのデバッグなどのたくさんの素敵な機能を持っています。Pythonwin は Python for Windows Extensions プロジェクトの一部、あるいは ActivePython ディストリビューション (https://www.activestate.com/activepython を参照) の一部として利用可能です。
Boa Constructor は、wxWidgets を使った IDE と GUI ビルダーです。これは視覚フレームの作成と操作、オブジェクト検査、オブジェクトブラウザのような多くのビュー、継承構造、doc string から生成される html ドキュメント、高度なデバッガ、総合ヘルプ、Zope のサポートを提供します。
Eric は PyQt や Scintilla editing component をもとにした IDE です。
Pydb は標準のデバッガである pdb を人気のグラフィカルデバッガフロントエンドである DDD (Data Display Debugger) とともに使うために改変したものです。Pydb は http://bashdb.sourceforge.net/pydb/ に、 DDD は https://www.gnu.org/software/ddd にあります。
商業のグラフィカルデバッガ付き Python IDE もあります。例えば:
- Wing IDE (https://wingware.com/)
- Komodo IDE (https://komodoide.com/)
- PyCharm (https://www.jetbrains.com/pycharm/)
バグの発見や静的分析に役立つツールはありますか?¶
はい。
PyChecker は Python ソースコードのバグを発見しコードの複雑さとスタイルについて警告する静的解析ツールです。PyChecker は http://pychecker.sourceforge.net/ から手に入ります。
Pylint も、モジュールがコーディング標準を満たすかを調べ、プラグインを書いてカスタム機能を加えられるようにするツールです。PyChecker が行うバグチェックに加え、 Pylint は行の長さ、変数名が一貫しているか、宣言されたインタフェースが完全に実装されているか、などを確かめる追加の機能を提供します。 https://docs.pylint.org/ で Pylint の機能の一覧を見られます。
どうしたら Python スクリプトからスタンドアロンバイナリを作れますか?¶
ユーザがダウンロードや起動のために Python ディストリビューションをインストールしなくてもよいスタンドアロンプログラムのためだけなら、Python を C コードにコンパイルできる必要はありません。プログラムに対して必要なモジュールを選び、そのモジュールを Python バイナリに束縛して一つの実行可能ファイルにまとめる多くのツールがあります。
一つは freeze ツールで、Python ソースツリーに Tools/freeze
として含まれています。これは Python バイトコードを C 配列に変換します。すべてのモジュールを標準 Python モジュールにリンクされる新しいプログラムに埋め込む C コンパイラです。
これはあなたのソースの (両方の形式の) import 文を再帰的にスキャンして、import されたモジュールを標準の Python パスと (組み込みモジュールのある) ソースディレクトリから探します。そして Python で書かれたモジュールのバイトコードを C コード (marshal モジュールでコードオブジェクトに変換できる配列) に変換し、実際にそのプログラム内で使われている組み込みモジュールだけが含まれたカスタムメイドの設定ファイルを作成します。そして生成された C コードをコンパイルして Python インタプリタの残りとリンクし、元のスクリプトと全く同じように動作する自己充足的なバイナリを形成します。
もちろん、凍結には C コンパイラが必要です。C コンパイラを必要としない選択肢もあります。その一つは、Thomas Heller の py2exe (Windows 専用) です
他には Anthony Tuininga の cx_Freeze があります。
Python プログラムのためのコーディングスタンダードやスタイルガイドはありますか?¶
はい。標準ライブラリモジュールに求められるコーディングスタイルは PEP 8 として文書化されています。
プログラムが遅すぎます。どうしたら速くなりますか?¶
一般に、それは難しい質問です。Python コードを速くするためには、いろいろな手法があります。最終手段として一部を C で書き直す事も考えてください。
Python を自動的に C や x86 アセンブリ言語に変換できる場合もあります。この場合、速度を上げるためにコードを変更する必要はありません。
Pyrex はPython コードの少し変化した版を C 拡張にコンパイルでき、多様なプラットフォームで使えます。(訳注: Pyrex は Cython に取って代わられています。)
Psyco は Python コードを x86 アセンブリ言語に変換する実行時コンパイラです。これを使うことが出来れば、重要な関数を劇的にスピードアップできます。
あとは、Python コードからもう少し速度を搾り出すための様々な手法について議論することになります。コード中の特定の関数が処理が集中するホットスポットで、最適化が必要であると認められない限り、決して いかなる最適化の手法も使わないでください。最適化はたいていコードを分かりづらくするので、分かりづらさのコスト (開発時間の延長とバグの可能性の増大) がそれに見合ったパフォーマンスの向上につながらないのであれば元が取れません。
performance tips に関するページが wiki にあります。
Guido van Rossum は https://www.python.org/doc/essays/list2str で最適化に関する逸話を詳述しています。
なお、関数や(特に)メソッドの呼び出しはかなり高価です。インスタンス変数を get や set したり他のメソッドを呼び出す程度の小さな関数がたくさんある純粋 OO インタフェースをデザインしているなら、インスタンス変数に直接アクセスするようなもっと直接的な方法も考えてみてください。また、どのプログラムが実行時間の大部分を占めているかを見つける標準モジュール profile
も参照してください (ちょっと忍耐できればの話ですが - プロファイリングはそれ自体がプログラムを一桁ほど遅くしてしまいます)。
もちろん、他のプログラミングの経験から得られた多くの標準的な最適化の発見的手法は Python にもよく当てはまることが多いです。たとえば、出力装置に出力を送るときに、一度に少なく書くよりもむしろ多く書いたほうが、カーネルのシステムコールのオーバーヘッドを減らすことができて、速くなるでしょう。したがって、CGI スクリプトは "一発" ですべて書き出すもののほうが小さなたくさんの出力に分けて書き出すものよりも速くなるでしょう。
また、必ず Python のコアな機能を適切に使ってください。例えば、スライシングなら、リストや他のシーケンスオブジェクトを、高度に最適化された C 実装で、インタプリタのメインループの一刻みで細切れにできます。こうして効果を得ることができる例は:
L2 = []
for i in range(3):
L2.append(L1[i])
こう使えばずっと短く、ずっと速くできます:
L2 = list(L1[:3]) # "list" is redundant if L1 is a list.
関数指向組み込み関数 map()
や zip()
なども一つのタスクを実行するためのループを加速するのに便利であることに注意してください。例えば、二つのリストの要素を組み合わせるためには:
>>> zip([1, 2, 3], [4, 5, 6])
[(1, 4), (2, 5), (3, 6)]
また、正弦を一度に計算するには:
>>> map(math.sin, (1, 2, 3, 4))
[0.841470984808, 0.909297426826, 0.14112000806, -0.756802495308]
このような場合には素早く演算が完了します。
その他の例には、 文字列オブジェクトのメソッド join()
、 split()
などが挙げられます。例えば s1..s7 が大きな (10K+) 文字列の時、 "".join([s1,s2,s3,s4,s5,s6,s7])
は単純に s1+s2+s3+s4+s5+s6+s7
とするよりもはるかに速くなるでしょう。なぜなら、 join()
はすべてのコピーを一括して行うのに対し、「足し算」が多くの副演算を行うからです。文字列を扱うには、 文字列オブジェクトのメソッド replace()
、 format()
を使ってください。正規表現を使うのは、決まった文字列のパターンを使わない時だけにしてください。 旧式の % 演算 string % tuple
と string % dictionary
も使えます。
ソートには必ずビルトインオブジェクトの list.sort()
を使ってください。また、 sorting mini-HOWTO の少し高度な使い方の例を参照してください。 list.sort()
は、よほど極端な状況でない限り、他のソートの技術に勝ります。
「ループを関数やメソッドの中に入れ込む」というのも一般的な手法です。例えば、遅いプログラムがあって、Python の ff()
関数が何度も呼ばれていることがプロファイラで分かったとします。この ff()
def ff(x):
... # do something with x computing result...
return result
がこのようなループで呼ばれる傾向がある気付いたとして:
list = map(ff, oldlist)
もしくは:
for x in sequence:
value = ff(x)
... # do something with value...
ff()
を書き換えて:
def ffseq(seq):
resultseq = []
for x in seq:
... # do something with x computing result...
resultseq.append(result)
return resultseq
また、上の二つの例を、 list = ffseq(oldlist)
と:
for value in ffseq(sequence):
... # do something with value...
のように書き換えることによって、関数を呼ぶためのオーバーヘッドを省けることが多いです。ff(x)
を一回だけ呼ぶ場合、 ffseq([x])[0]
に直してしまうとちょっと不利になります。 もちろん、このテクニックがいつでも適切であるわけではありませんし、解決のための他の方法もあります。
関数やメソッドの探索の結果をローカル変数に明示的に保存すると少しパフォーマンスが良くなります。次のようなループ:
for key in token:
dict[key] = dict.get(key, 0) + 1
は、繰り返しのたびに dict.get
を求めています。 このメソッドが変わることがないのなら、少し速い実装は:
dict_get = dict.get # look up the method once
for key in token:
dict[key] = dict_get(key, 0) + 1
デフォルト引数は、実行時でなく、コンパイル時に値を一回で決めてしまうのに使えます。これは、プログラムの実行中に変化しない関数やオブジェクト、例えば:
def degree_sin(deg):
return math.sin(deg * math.pi / 180.0)
を、次のように置き換えるときにのみ行えます:
def degree_sin(deg, factor=math.pi/180.0, sin=math.sin):
return sin(deg * factor)
この手法はデフォルト引数が変えられないことを前提に使うので、ユーザーが API で混乱するおそれがないときのみ使えます。
コア言語¶
なぜ変数に値があるのに UnboundLocalError が出るのですか?¶
もともと動いていたコードが、関数の本体のどこかに代入文を加えるという変更をしたら UnboundLocalError を出すのには驚くかもしれません。
このコード:
>>> x = 10
>>> def bar():
... print x
>>> bar()
10
は動きますが、このコード:
>>> x = 10
>>> def foo():
... print x
... x += 1
は UnboundLocalError になります:
>>> foo()
Traceback (most recent call last):
...
UnboundLocalError: local variable 'x' referenced before assignment
これは、あるスコープの中で変数に代入を行うとき、その変数はそのスコープに対してローカルになり、外のスコープにある同じ名前の変数を隠すからです。 foo の最後の文が x
に新しい値を代入しているので、コンパイラはこれをローカル変数であると認識します。その結果、先の print x
が初期化されていないローカル変数を表示しようとして結果はエラーとなります。
上の例では、グローバルであると宣言することで外のスコープにアクセスできます:
>>> x = 10
>>> def foobar():
... global x
... print x
... x += 1
>>> foobar()
10
この明示的な宣言は (表面的には似ているクラスとインスタンス変数の例とは違って) あなたは実際は他のスコープの変数の値を変えようとしているのだ、ということを知らせるのに必要です:
>>> print x
11
Python のローカルとグローバル変数のルールは何ですか?¶
Python では、関数の中で参照のみされる変数は暗黙のうちにグローバルになります。関数の本体のどこかで値が変数に代入されたなら、それは明示的にグローバルであると宣言されない限り、ローカルであるとみなされます。
最初はちょっと驚くでしょうが、少し考えると納得できます。一方では、代入された変数に global
を要求することで、意図しない副作用を防げます。他方では、グローバルな参照の度に global
が要求されてしまうと、 global
を使ってばかりになってしまいます。ビルトイン関数やインポートされたモジュールの内容を参照するたびにグローバル宣言をしなければならないのです。その乱雑さは副作用を特定するための global
宣言の便利さよりも重大です。
ループの中で異なる値で定義されたラムダ式が、同じ値を返すのはなぜですか?¶
for ループを使って、少しずつ異なるラムダを定義 (もしくは簡単な関数) するとします。例えば:
>>> squares = []
>>> for x in range(5):
... squares.append(lambda: x**2)
これで x**2
を計算する 5 つのラムダのリストが得られます。それらを呼び出したとき、それぞれ 0
、1
、4
、9
、16
を返すと予想するかもしれません。しかし実際にやってみると、全て 16
が返ってくるのを目にするでしょう:
>>> squares[2]()
16
>>> squares[4]()
16
これは、x
がラムダにとってのローカル変数ではなく外側のスコープで定義されていて、ラムダが定義されたときでなく呼び出されたときにアクセスされるために起こります。ループが終わった時点では x
は 4
であり、従って、全ての関数は 4**2
つまり 16
を返します。このことは x
の値を変えてみることで検証でき、ラムダの返り値がどのように変わるのか観察できます:
>>> x = 8
>>> squares[2]()
64
これを避けるためには、グローバルの x
の値に依存しないために、ラムダにとってのローカル変数に値を保存する必要があります:
>>> squares = []
>>> for x in range(5):
... squares.append(lambda n=x: n**2)
ここで、n=x
は新しいラムダにとってのローカル変数 n
を作成し、ラムダが定義されるときに計算されるので、ループのその時点での x
と同じ値を持っています。これは、1 つ目のラムダでは n
の値は 0
になり、2 つ目では 1
、3 つ目では 2
以下同様、となることを意味します。従って、それぞれのラムダは今や正しい値を返すようになりました:
>>> squares[2]()
4
>>> squares[4]()
16
この動作はラムダに特有なものではなく、通常の関数にも適用されることに注意してください。
モジュールで import を使う際の「ベストプラクティス」は何ですか?¶
一般的に from modulename import *
を使ってはいけません。そのようにするとインポータの名前空間は汚染され、linter が未定義の名前を発見することが難しくなります。
モジュールはファイルの先頭でインポートしてください。これによってコードが必要とする他のモジュールが明確になり、モジュール名がスコープに含まれるかどうかに迷わなくなります。行に一つのインポートにすると、モジュールのインポートの追加と削除が容易になりますが、行に複数のインポートにすると画面の領域が少なく済みます。
次の手順でモジュールをインポートするのが、良いプラクティスになります:
- 標準ライブラリモジュール – 例
sys
、os
、getopt
、re
- サードパーティのライブラリモジュール (Python の site-packages ディレクトリにあるもの) – 例 mx.DateTime、ZODB、PIL.Image、など
- 自前で開発したモジュール
明示的な相対インポートだけを使ってください。 package.sub.m1
モジュールのコードを書いていて、 package.sub.m2
をインポートしようとするときに import m2
だけで済ませるのはやめてください。たとえそれが合法でも。代わりに from package.sub import m2
または from . import m2
と書いてください。
循環参照の問題を避けるために、インポートを関数やクラスに移すことが必要なときもあります。Gordon McMillan によれば:
循環参照は両方のモジュールが "import <module>" 形式のインポートを使っていれば大丈夫です。二つ目のモジュールが最初のモジュールから名前を確保しようとして ("from module import name")、そのインポートがトップレベルにあると駄目です。最初のモジュールが二つ目のモジュールをインポートするのに忙しくて、最初のモジュールの名前が利用可能になっていないからです。
この状況では、二つ目のモジュールが一つの関数の中でのみ使われているならば、そのインポートは簡単に関数の中に移せます。インポートが呼ばれたとき、最初のモジュールは初期化を完了していて、二つ目のモジュールは自分のインポートをできます。
プラットフォーム依存のモジュールがあるときには、インポートをトップレベルの外に動かすことも必要です。この場合、ファイルの先頭ではすべてのモジュールをインポートすることさえできないかもしれません。この場合は、対応するプラットフォームに合わせたコードで正しいモジュールをインポートすることを選ぶと良いです。
循環参照の問題を避けたりモジュールの初期化にかかる時間を減らしたりしたいなら、単にインポートを関数定義の中などのローカルなスコープに移してください。この手法は多くのインポートがプログラムがどのように実行されるかに依存しなくてよいときに特に有効です。ある関数の中でのみモジュールが使われるのなら、インポートをその関数の中に移すことを考えてもいいでしょう。なお、モジュールを読み込む最初の回はモジュールの初期化の時間のために高価になりえますが、複数回目にモジュールを読み込むのは事実上無料、辞書探索の数回のコストだけで済みます。モジュール名がスコープから外れてさえ、そのモジュールはおそらく sys.modules
から利用できるでしょう。
オプションパラメータやキーワードパラメータを関数から関数へ渡すにはどうしたらいいですか?¶
関数のパラメータリストに引数を *
と **
指定子 (specifier) で集めてください。そうすれば、固定引数をタプルとして、キーワード引数を辞書として得られます。これで、他の関数を呼び出すときに *
と **
を使ってそれらの引数を渡せます:
def f(x, *args, **kwargs):
...
kwargs['width'] = '14.3c'
...
g(x, *args, **kwargs)
あまりありませんが、Python の 2.0 以前のバージョンを考慮するときは、代わりに apply()
を使ってください:
def f(x, *args, **kwargs):
...
kwargs['width'] = '14.3c'
...
apply(g, (x,)+args, kwargs)
実引数と仮引数の違いは何ですか?¶
仮引数 (parameter) は関数定義に表れる名前で定義されるのに対し、 実引数 (argument) は関数を呼び出すときに実際に渡す値のことです。仮引数は関数が受け取ることの出来る実引数の型を定義します。例えば、以下のような関数定義があったとして:
def func(foo, bar=None, **kwargs):
pass
foo、bar、kwargs は func
の仮引数です。一方、func
を呼び出すときには、例えば:
func(42, bar=314, extra=somevar)
42
、314
、somevar
という値は実引数です。
なぜ list 'y' を変更すると list 'x' も変更されるのですか?¶
次のようなコードを書いたとします:
>>> x = []
>>> y = x
>>> y.append(10)
>>> y
[10]
>>> x
[10]
どうして y
への要素の追加が x
も変更してしまうのか疑問に思うかもしれません。
このような結果になる2つの要因があります:
- 変数とは、単にオブジェクトを参照するための名前に過ぎません。
y = x
とすることは、リストのコピーを作りません – それはx
が参照するのと同じオブジェクトを参照する新しい変数y
を作ります。つまり、あるのは一つのオブジェクト(この場合リスト)だけであって、x
とy
の両方がそれを参照しているのです。 - リストは mutable です。内容を変更出来る、ということです。
append()
呼び出しの後、ミュータブルオブジェクトの内容が []
から [10]
に変わります。 変数が同じオブジェクトを参照しているので、どちらの名前であっても変更された値 [10]
にアクセスします。
代わりに x
にイミュータブルを代入すると:
>>> x = 5 # ints are immutable
>>> y = x
>>> x = x + 1 # 5 can't be mutated, we are creating a new object here
>>> x
6
>>> y
5
この場合ご覧の通り x
と y
はまったく同じではありませんね。これは整数が immutable だからで、 x = x + 1
は整数の 5
の値を変更しているのではありません; 代わりに新しいオブジェクト(整数 6
)を作って x
に代入しています (つまり x
が参照するオブジェクトが変わります)。この代入の後では私たちは 2 つのオブジェクト(整数の 6
と 5
)を持っていて、2 つの変数はそれらを参照しています(x
はいまや 6
を参照していますが y
は 5
を参照したままです)。
ある演算 (たとえば y.append(10)
, y.sort()
) がオブジェクトを変更する一方で、外見上は似た演算 (たとえば y = y + [10]
, sorted(y)
) は新しいオブジェクトを作ります。Python では一般に (そして標準ライブラリの全てのケースで)、このような 2 つのタイプの演算にまつわる混乱を避けるために、オブジェクトを変更するメソッドは None
を返します。ですからもしあなたが誤って y
の複製の並び替えをするつもりで y.sort()
と書いた場合に結果手にするのは None
でしょうから、あなたのプログラムは簡単に診断出来るエラーを起こすでしょう。
しかしながら、同じ操作が型ごとに異なる振る舞いをする演算の種類が一つあります: 累算代入演算です。例えば +=
はリストを変更しますが、タプルや整数は変更しません(a_list += [1, 2, 3]
は a_list.extend([1, 2, 3])
と同じ意味で、そして a_list
を変更しますが、 some_tuple += (1, 2, 3)
と some_int += 1
は新しいオブジェクトを作ります)。
言い換えると:
- ミュータブルなオブジェクト(
list
,dict
,set
, 等)を持っている場合、私たちはその内容を変更するある種の演算を使うことが出来、それを参照している全ての変数はその変化を見ることになるでしょう。 - イミュータブルなオブジェクト(
str
,int
,tuple
, 等)を持っている場合、それを参照している全ての変数は同じ値を参照しているでしょうが、持っている値を新しい値に変換する演算はいつでも新しいオブジェクトを返します。
2つの変数が同じオブジェクトを参照しているかどうかが知りたければ、 is
オペレータまたは組み込み関数 id()
が使えます。
パラメータを出力する関数 (参照渡し) はどのように書きますか?¶
前提として、Python では引数は代入によって渡されます。代入はオブジェクトへの参照を作るだけなので、呼び出し元と呼び出し先にある引数名の間にエイリアスはありませんし、参照渡しそれ自体はありません。望む効果を得るためには幾つかの方法があります。
結果のタプルを返すことによって:
def func2(a, b): a = 'new-value' # a and b are local names b = b + 1 # assigned to new objects return a, b # return new values x, y = 'old-value', 99 x, y = func2(x, y) print x, y # output: new-value 100
これはたいてい一番明確な方法です。
グローバル変数を使って。これはスレッドセーフでないので、推奨されません。
ミュータブルな (インプレースに変更可能な) オブジェクトを渡すことによって:
def func1(a): a[0] = 'new-value' # 'a' references a mutable list a[1] = a[1] + 1 # changes a shared object args = ['old-value', 99] func1(args) print args[0], args[1] # output: new-value 100
変更される辞書に渡すことによって:
def func3(args): args['a'] = 'new-value' # args is a mutable dictionary args['b'] = args['b'] + 1 # change it in-place args = {'a': 'old-value', 'b': 99} func3(args) print args['a'], args['b']
または、クラスインスタンスに値を同梱することによって:
class callByRef: def __init__(self, **args): for (key, value) in args.items(): setattr(self, key, value) def func4(args): args.a = 'new-value' # args is a mutable callByRef args.b = args.b + 1 # change object in-place args = callByRef(a='old-value', b=99) func4(args) print args.a, args.b
このような複雑なことをする理由はめったに無いでしょう。
一番の選択は、複数の結果を含むタプルを返すことです。
Python で高次関数はどのようにつくりますか?¶
二つの方法があります: ネストされたスコープを使う方法と、呼び出し可能オブジェクトを使う方法です。例えば、a*x+b
の値を計算する f(x)
関数を返す linear(a,b)
を定義したいとします。ネストされたスコープを使うと:
def linear(a, b):
def result(x):
return a * x + b
return result
また、呼び出し可能オブジェクトを使うと:
class linear:
def __init__(self, a, b):
self.a, self.b = a, b
def __call__(self, x):
return self.a * x + self.b
どちらの場合でも、
taxes = linear(0.3, 2)
とすれば、taxes(10e6) == 0.3 * 10e6 + 2
となるような呼び出し可能オブジェクトを得られます。
呼び出し可能オブジェクトを使う方法は、少し遅くなり、わずかにコードが長くなるという短所があります。ですが、継承を使ってコーラブル同士で記号を共有することもできます:
class exponential(linear):
# __init__ inherited
def __call__(self, x):
return self.a * (x ** self.b)
オブジェクトはいくつかのメソッドに状態をカプセル化できます:
class counter:
value = 0
def set(self, x):
self.value = x
def up(self):
self.value = self.value + 1
def down(self):
self.value = self.value - 1
count = counter()
inc, dec, reset = count.up, count.down, count.set
ここで、inc()
、dec()
、reset()
は同じカウント変数を共有する関数のようにふるまいます。
Python のオブジェクトはどのようにコピーしますか?¶
一般的に、普通は copy.copy()
や copy.deepcopy()
を試してください。何でもコピーできるとは限りませんが、たいていはできます。
もっと簡単にコピーできるオブジェクトもあります。辞書には copy()
メソッドがあります:
newdict = olddict.copy()
シーケンスはスライシングでコピーできます:
new_l = l[:]
オブジェクトのメソッドや属性はどのように見つけますか?¶
ユーザー定義クラスのインスタンス x で、dir(x)
はインスタンス属性とそのクラスで定義されたメソッドや属性を含む名前のアルファベット順リストを返します。
コードはどのようにオブジェクトの名前を見つけるのですか?¶
概して、オブジェクトは本当は名前を持たないので、見つけることはできません。本質的には、代入とはいつも値に名前を束縛することです。def
と class
文も同じですが、この場合は値はコーラブルです。以下のコードを考えてみましょう:
>>> class A:
... pass
...
>>> B = A
>>> a = B()
>>> b = a
>>> print b
<__main__.A instance at 0x16D07CC>
>>> print a
<__main__.A instance at 0x16D07CC>
おそらく、このクラスには名前があります。このクラスは二つの名前に縛られて、名前 B を通して呼び出されますが、それでもクラス A のインスタンスとして報告されるのです。しかし、両方の名前が同じ値に束縛されている以上、このインスタンスの名前が a か b か決めることはできないのです。
概して、コードにとってある値の「名前を知っている」事は重要ではありません。あなたがわざと内省的なコードを書いているのでない限り、方針を変えた方がいいかもしれないということになるでしょう。
comp.lang.python で、Fredrik Lundh はこの問題の答えとして素晴らしい喩えをしてくれました:
玄関にいた猫の名前を知るのと同じ方法です: その猫 (オブジェクト) 自体はその名前を言うことができないし、それは実は問題ではありません – その猫が何と呼ばれているかを知る唯一の方法は、すべての隣人 (名前空間) にその猫 (オブジェクト) が何と呼ばれているかを聞くことです。
……そして、その猫が沢山の名前で知られていたり、逆に全く名前が無かったりしても驚かないでください!
カンマ演算子はなぜ優先されるのですか?¶
カンマは Python では演算子ではありません。このセッションを考えてください:
>>> "a" in "b", "a"
(False, 'a')
カンマは演算子ではなく、式の分離子なので、上の式は次の式と同じように評価されます:
("a" in "b"), "a"
こうではありません:
"a" in ("b", "a")
他のさまざまな演算子(=
、+=
など)も同じです。これらは真の演算子ではありませんが、代入文の構文上のデリミタです。
C の "?:" 三項演算子と等価なものはありますか?¶
はい、この機能は Python 2.5 で追加されました。構文は以下のようになります:
[on_true] if [expression] else [on_false]
x, y = 50, 25
small = x if x < y else y
2.5 以前のバージョンについては、これの答えは「いいえ」です。
Python で解し難いワンライナーを書くことはできますか?¶
はい。そういうものはたいてい、 lambda
の中に lambda
がネストされています。Ulf Bartelt による下の3つの例を見てください:
# Primes < 1000
print filter(None,map(lambda y:y*reduce(lambda x,y:x*y!=0,
map(lambda x,y=y:y%x,range(2,int(pow(y,0.5)+1))),1),range(2,1000)))
# First 10 Fibonacci numbers
print map(lambda x,f=lambda x,f:(f(x-1,f)+f(x-2,f)) if x>1 else 1: f(x,f),
range(10))
# Mandelbrot set
print (lambda Ru,Ro,Iu,Io,IM,Sx,Sy:reduce(lambda x,y:x+y,map(lambda y,
Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,Sy=Sy,L=lambda yc,Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,i=IM,
Sx=Sx,Sy=Sy:reduce(lambda x,y:x+y,map(lambda x,xc=Ru,yc=yc,Ru=Ru,Ro=Ro,
i=i,Sx=Sx,F=lambda xc,yc,x,y,k,f=lambda xc,yc,x,y,k,f:(k<=0)or (x*x+y*y
>=4.0) or 1+f(xc,yc,x*x-y*y+xc,2.0*x*y+yc,k-1,f):f(xc,yc,x,y,k,f):chr(
64+F(Ru+x*(Ro-Ru)/Sx,yc,0,0,i)),range(Sx))):L(Iu+y*(Io-Iu)/Sy),range(Sy
))))(-2.1, 0.7, -1.2, 1.2, 30, 80, 24)
# \___ ___/ \___ ___/ | | |__ lines on screen
# V V | |______ columns on screen
# | | |__________ maximum of "iterations"
# | |_________________ range on y axis
# |____________________________ range on x axis
よい子はまねしないでね!
数と文字列¶
十六進数や八進数を指定するにはどうしたらいいですか?¶
八進数を指定するには、八進数での値の先頭に 0 と "o" (小文字または大文字) を加えてください。たとえば、変数 "a" に八進数での "10" (十進数での"8") を代入するには、こう打ってください:
>>> a = 0o10
>>> a
8
十六進数も簡単です。ただ十六進数での値の先頭に 0 と "x" (小文字または大文字) を加えてください。十六進数は小文字でも大文字でも指定できます。たとえば、Python インタプリタで:
>>> a = 0xa5
>>> a
165
>>> b = 0XB2
>>> b
178
なぜ -22 // 10 は -3 を返すのですか?¶
i % j
が j
と同じ符号であってほしいことに基づいています。それに加えて以下のようにもしたいとすると:
i == (i // j) * j + (i % j)
整数除算は床を返すことになります。C にも C の一貫性があって、i % j
が i
と同じ符号を持つように i // j
を丸めています。
i % j
は、j
が負の時には実際にはほとんど使いません。j
が正なら、たくさん使います。その事実上すべての場合、i % j
は >= 0
となる方が便利です。時計が 10 時を指している時、その 200 時間前は何時でしょうか。-190 % 12 == 2
となるのが便利です。-190 % 12 == -10
は噛み付きかねないバグです。
注釈
Python 2 では、 __future__.division
が有効でなければ、a / b
は a // b
と同じ結果を返します。これは "古典的な (classic)" 除算とも呼ばれます。
文字列を数に変換するにはどうしたらいいですか?¶
整数に変換するには、組み込みの int()
型コンストラクタを使ってください。例えば、 int('144') == 144
です。同様に、 float()
は浮動小数点に変換します。例えば、 float('144') == 144.0
です。
デフォルトでは、これらは数を十進数として解釈するので、 int('0o144')
や int('0x144')
は ValueError
を送出します。 int(string, base)
はオプションの第二引数をとって変換元の基数にします。つまり int('0x144', 16) == 324
です。基数が 0 と指定された場合、その数は Python の基準によって解釈されます。先頭が '0o' なら八進数で、'0x' なら十六進数を表します。
文字列を数に変換するだけのために eval()
を使わないでください。 eval()
は特に遅いですし、セキュリティ上のリスクもあります。求められない副作用を持つような Python の式を渡そうとする人がいるかも知れません。例えば、あなたのホームディレクトリを消去する __import__('os').system("rm -rf $HOME")
を渡そうとする人がいるかも知れません。
eval()
にも数を Python の式として解釈する機能があります。だから例えば、 eval('09')
は構文エラー起こします。Python は '0' で始まる数を八進数 (基数 8) とみなすからです。
数を文字列に変換するにはどうしたらいいですか?¶
例えば、144 という数を '144' という文字列に変換したいなら、組み込みの型コンストラクタ str()
を使ってください。十六進数や八進数にしたければ、組み込み関数の hex()
や oct()
を使ってください。装飾された形式にするには、 書式指定文字列の文法 の項を参照してください。例えば、 "{:04d}".format(144)
は '0144'
になり、 "{:.3f}".format(1/3)
は '0.333'
になります。文字列に % 演算子 を使うこともできます。詳細はライブラリリファレンスの解説を参照してください。
文字列をインプレースに変更するにはどうしたらいいですか?¶
文字列はイミュータブルなので、変更することはできません。それができるオブジェクトを作るには、その文字列をリストに変換してみるか、array モジュールを使ってください:
>>> import io
>>> s = "Hello, world"
>>> a = list(s)
>>> print a
['H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd']
>>> a[7:] = list("there!")
>>> ''.join(a)
'Hello, there!'
>>> import array
>>> a = array.array('c', s)
>>> print a
array('c', 'Hello, world')
>>> a[0] = 'y'; print a
array('c', 'yello, world')
>>> a.tostring()
'yello, world'
関数やメソッドを呼ぶのに文字列を使うにはどうしたらいいですか?¶
様々なテクニックがあります。
一番いいのは、文字列を関数に対応させる辞書を使うことです。このテクニックの一番の利点は、文字列が関数の名前と同じ必要がないことです。この方法は case 構造をエミュレートするための一番のテクニックでもあります:
def a(): pass def b(): pass dispatch = {'go': a, 'stop': b} # Note lack of parens for funcs dispatch[get_input()]() # Note trailing parens to call function
組み込み関数の
getattr()
を使う方法:import foo getattr(foo, 'bar')()
なお、
getattr()
はクラス、クラスインスタンス、モジュールなど、どんなオブジェクトにも使えます。これは標準ライブラリでも何箇所か使われています。このように:
class Foo: def do_foo(self): ... def do_bar(self): ... f = getattr(foo_instance, 'do_' + opname) f()
locals()
やeval()
を使って関数名を決める方法:def myFunc(): print "hello" fname = "myFunc" f = locals()[fname] f() f = eval(fname) f()
ノート:
eval()
の使用は遅いし危険です。もしあなたが文字列の内容を絶対的に支配できなければ、任意の関数を実行されるようにする文字列を渡す人がいるかも知れません。
文字列から後端の改行を取り除く Perl の chomp() に相当するものはありますか?¶
Python 2.2 からは、 S.rstrip("\r\n")
を使って文字列 S
の終端から他の空白文字を取り除くことなくすべての行末記号を取り除くことができます。文字列 S
が複数行を表し、終端に空行があるとき、そのすべての空行も取り除かれます:
>>> lines = ("line 1 \r\n"
... "\r\n"
... "\r\n")
>>> lines.rstrip("\n\r")
'line 1 '
これは典型的に一度に一行ずつテキストを読みたい時にのみ使われるので、S.rstrip()
をこの方法で使うとうまくいきます。
古いバージョンの Python では、部分的な代用品が二つあります:
- すべての終端の空白文字を取り除きたいなら、文字列オブジェクトの
rstrip()
メソッドを使ってください。これは改行記号一つだけでなく、すべての終端の空白文字を取り除きます。 - そうでなく、文字列
S
に一行しか無いなら、S.splitlines()[0]
を使ってください。
scanf() や sscanf() と同等なものはありますか?¶
そのようなものはありません。
簡単な入力解析で、多くの場合に一番簡単な方法は、文字列オブジェクトの split()
メソッドで行を空白文字で区切られた単語に分け、十進数の文字列を int()
や float()
で数値に変換することです。 split()
にはオプションの "sep" 変数があり、行に空白文字以外の区切りを使っているときに便利です。
もっと複雑な入力解析をしたいなら、C の sscanf()
よりも正規表現の方が便利ですし、この処理に向いています。
'UnicodeError: ASCII [decoding,encoding] error: ordinal not in range(128)' とはどういう意味ですか?¶
このエラーは、あなたの Python インストールが 7-bit ASCII 文字列しか扱えないことを表します。この問題を扱うには二つの方法があります。
あなたのプログラムが、任意の文字セットエンコーディングのデータを扱わなければならないなら、一般に、アプリケーションが起動する環境によってデータのエンコーディングが特定されます。入力をそのエンコーディングを使って Unicode に変換する必要があります。例えば、email や web 入力を扱うプログラムは、概して文字セットエンコーディング情報を Content-Type ヘッダから見つけます。これに使うことで、入力データを Unicode に正しく変換できます。 value
によって参照される文字列が UTF-8 でエンコードされているとすれば:
value = unicode(value, "utf-8")
は Unicode オブジェクトを返します。データが UTF-8 に正しく変換されないなら、上記の呼び出しは UnicodeError
例外を送出します。
非 ASCII データを持つ文字列を Unicode に変換すればいいだけなら、まず ASCII エンコーディングを仮定して変換し、失敗したら Unicode オブジェクトを生成すればいいです:
try:
x = unicode(value, "ascii")
except UnicodeError:
value = unicode(value, "utf-8")
else:
# value was valid ASCII data
pass
デフォルトのエンコーディングは、Python ライブラリの一部である sitecustomize.py
と呼ばれるファイルで設定できます。しかし、Python 全体におけるデフォルトのエンコーディングを変えてしまうことは、サードパーティ拡張モジュールの失敗につながるのでお勧めできません。
なお、Windows には、 "mbcs" として知られるエンコーディングがあり、これはあなたのロケールに依存するエンコーディイングを使います。多くの場合、特に COM で作業をするとき、これが使うのに適したデフォルトのエンコーディングです。
シーケンス(タプル/リスト)¶
タプル、リスト間の変更はどのようにするのですか?¶
型コンストラクタ tuple(seq)
はすべてのシーケンス (実際には、すべてのイテラブル) を同じ要素、同じ順序のタプルに変換します。
例えば、 tuple([1, 2, 3])
は (1, 2, 3)
を与え、 tuple('abc')
は ('a', 'b', 'c')
を与えます。引数がタプルなら、コピーを作らずに引数のオブジェクトそのものを返すので、あるオブジェクトが既にタプルになっているか確信が持てないのなら、 tuple()
を呼ぶのが手軽です。
型コンストラクタ list(seq)
はすべてのシーケンスあるいはイテラブルを同じ要素、同じ順序のリストに変換します。例えば、list((1, 2, 3))
は [1, 2, 3]
を与え、list('abc')
は ['a', 'b', 'c']
を与えます。引数がリストなら、seq[:]
と同様にコピーを作ります。
インデクスが負の場合はどうなりますか?¶
Python のシーケンスは正の数と負の数でインデクスされます。正の数では、0 が最初のインデクス、1 が 2 番目のインデクス、以下も同様です。負のインデクスでは、-1 が最後のインデクス、-2 が最後から 2 番目のインデクス、以下も同様です。seq[-n]
は seq[len(seq)-n]
と同じだと考えてください。
負のインデクスを使うと便利なことがあります。例えば、S[:-1]
は文字列の最後以外のすべての文字を表すので、文字列の末尾の改行を取り除くときに便利です。
シーケンスを逆順にイテレートするにはどうしたらいいですか?¶
Python 2.4 で追加された reversed()
を使ってください:
for x in reversed(sequence):
... # do something with x ...
これは元のシーケンスをいじるのではなく、逆順の新しいコピーを作ってイテレートさせます。
Python 2.3 では、拡張スライス構文を使います:
for x in sequence[::-1]:
... # do something with x ...
リストから重複を取り除くにはどうしますか?¶
Python Cookbook の長い議論に多くの方法があるので参照してください:
リストを並び替えて構わないのなら、ソートした上でリストの最初から最後までを調べ、次のように重複を削除してください:
if mylist:
mylist.sort()
last = mylist[-1]
for i in range(len(mylist)-2, -1, -1):
if last == mylist[i]:
del mylist[i]
else:
last = mylist[i]
リストのすべての要素が辞書のキーとして使える (つまり、すべての要素が hashable) なら、おそらくこのほうが速いです:
d = {}
for x in mylist:
d[x] = 1
mylist = list(d.keys())
Python 2.5 以上では、以下を代わりに使えます:
mylist = list(set(mylist))
リストを集合に変換するときに重複は取り除かれるので、それをリストに戻せばいいのです。
Python で配列を作るにはどうしますか?¶
リストを使ってください:
["this", 1, "is", "an", "array"]
リストの時間計算量は C や Pascal の配列と同じです。大きな違いは、Python のリストは多くの異なる型のオブジェクトを含めることです。
array
モジュールにも固定された型を簡潔に表現する配列を作るためのメソッドがありますが、リストよりもインデクスが遅いです。また、Numeric 拡張その他でも、様々な特徴をもつ配列的な構造体が定義されています。
Lisp 方式の連結リストを得るのに、タプルを使ってコンスセルをエミュレートできます:
lisp_list = ("like", ("this", ("example", None) ) )
ミュータブルな必要があるなら、タプルではなくリストを使いましょう。lisp の car にあたるものが lisp_list[0]
で、cdr にあたるものが lisp_list[1]
です。本当に必要だと確信できるとき以外はこれはしないでください。たいてい、これは Python のリストを使うよりも非常に遅いですから。
多次元のリストを作るにはどうしますか?¶
このようにして多次元の配列を作ろうとしてしまったことがあるでしょう:
>>> A = [[None] * 2] * 3
これを表示したときには問題なさそうに見えます:
>>> A
[[None, None], [None, None], [None, None]]
しかし値を代入すると、その値が複数の場所に現れてしまいます:
>>> A[0][0] = 5
>>> A
[[5, None], [5, None], [5, None]]
これは、*
を使ったリストの複製がコピーを作らず、存在するオブジェクトへの参照を作るだけだからです。この *3
は長さ 2 の同じリストへの参照を含むリストを作ります。一つの列に対する変更はすべての列に現れますが、これが望んだ結果であることはまずないでしょう。
おすすめの方法は、最初に望んだ長さのリストを作り、それから新しく作ったリストでそれぞれの要素を埋めていくことです:
A = [None] * 3
for i in range(3):
A[i] = [None] * 2
これは長さ 2 の異なるリスト 3 つを含むリストを生成します。リスト内包表記も使えます:
w, h = 2, 3
A = [[None] * w for i in range(h)]
あるいは、行列データ型を提供している拡張を使用することもできます; NumPy が最もよく知られています。
オブジェクトのシーケンスにメソッドを適用するにはどうしますか?¶
リスト内包表記を使ってください:
result = [obj.method() for obj in mylist]
もっと一般化したければ、以下の関数を試してみてください:
def method_map(objects, method, arguments):
"""method_map([a,b], "meth", (1,2)) gives [a.meth(1,2), b.meth(1,2)]"""
nobjects = len(objects)
methods = map(getattr, objects, [method]*nobjects)
return map(apply, methods, [arguments]*nobjects)
なぜ加算はされるのに a_tuple[i] += ['item'] は例外を送出するのですか?¶
これは、拡張代入演算子は 代入 演算子だ、という事実と、Python での可変オブジェクトと不変オブジェクトの違いが組み合わさって起きるのです。
この議論は一般的に、可変オブジェクトを指すタプルの要素に、拡張代入演算子が適用されたときにも適用できますが、例として list
と +=
を使います。
次のように書いたとします:
>>> a_tuple = (1, 2)
>>> a_tuple[0] += 1
Traceback (most recent call last):
...
TypeError: 'tuple' object does not support item assignment
例外が送出された理由は明らかです: 1
が (1
) を指すオブジェクト a_tuple[0]
に加えられ、結果のオブジェクト 2
が生成されますが、計算結果 2
をタプルの第 0
要素に代入しようとしたときに、エラーが発生します。なぜならば、タプルの要素が何を指すかは変えられないからです。
このような裏事情の元、拡張代入文はだいたい次のようなことをしています:
>>> result = a_tuple[0] + 1
>>> a_tuple[0] = result
Traceback (most recent call last):
...
TypeError: 'tuple' object does not support item assignment
タプルは不変なので、例外を生み出しているのは操作の代入部分なのです。
次のように書いたとします:
>>> a_tuple = (['foo'], 'bar')
>>> a_tuple[0] += ['item']
Traceback (most recent call last):
...
TypeError: 'tuple' object does not support item assignment
この例外にはちょっと驚きますが、もっと驚くべきことは、エラーがあったとしても追記はきちんと動いている、という事実です:
>>> a_tuple[0]
['foo', 'item']
なぜこれが起きるかを調べるためには、次の 2 点を知っている必要があります。(a) オブジェクトに __iadd__
特殊メソッドが実装されている場合、拡張代入 +=
が実行されるときにそれが呼び出され、その返り値が代入文で使われます; (b) リストでは、__iadd__
は extend
の呼び出しと等価で、リストを返します。こんな理由で、リストでは +=
は list.extend
の "略記" だと言ったのでした:
>>> a_list = []
>>> a_list += [1]
>>> a_list
[1]
これは次のと等価です:
>>> result = a_list.__iadd__([1])
>>> a_list = result
a_list が指していたオブジェクトは更新され、更新されたオブジェクトへのポインタは再度 a_list
に代入されます。代入しているのは、a_list
が更新前まで指していた同じオブジェクトへのポインタなので、代入は最終的には何もしていないのですが、代入処理自体は起きています。
従って、今のタプルの例では、次のと同じことが起きています:
>>> result = a_tuple[0].__iadd__(['item'])
>>> a_tuple[0] = result
Traceback (most recent call last):
...
TypeError: 'tuple' object does not support item assignment
__iadd__
は成功し、リストは拡張 (extend) されますが、result
が a_tuple[0]
が既に指しているオブジェクトと同じオブジェクトを指していたとしても、タプルは不変なので、その最後の代入はやはりエラーとなります。
辞書¶
一貫した順序でキーを表示する辞書はありますか?¶
できません。辞書はキーを予測できない順序で保存しているので、辞書の要素が表示される順序もまた予測できないのです。
ファイルに印字可能なバージョンを保存し、変更を加えてから他の印字された辞書と比較したい時に苛立たしいかもしれません。この場合は、 pprint
モジュールで辞書を整形して表示してください。要素がキーでソートされて表されます。
もっと複雑な解決策は、 dict
のサブクラスとして SortedDict
クラスを作り、それに予測可能な順序で自身を表示させることです。そのようなクラスの単純な実装の一つは:
class SortedDict(dict):
def __repr__(self):
keys = sorted(self.keys())
result = ("{!r}: {!r}".format(k, self[k]) for k in keys)
return "{{{}}}".format(", ".join(result))
__str__ = __repr__
これは完璧な解法とは程遠いですが、多くの状況でうまく働くでしょう。最大の欠点は、辞書内のどれかの値がまた辞書であった場合に、それらの値はどんな特定の順序でも表示されないことです。
複雑なソートがしたいのですが、Python でシュワルツ変換はできますか?¶
Perl コミュニティの Randal Schwartz の作とされるこのテクニックは、それぞれの要素に「ソート値」を対応付けるある尺度によって、リストの要素をソートします。
Python では、 sort()
関数で key
引数を使ってください:
Isorted = L[:]
Isorted.sort(key=lambda s: int(s[10:15]))
リストを別のリストの値によってソートするにはどうしますか?¶
二つのリストを混ぜあわせてタプルのリストにしてから、必要な要素を選んでください:
>>> list1 = ["what", "I'm", "sorting", "by"]
>>> list2 = ["something", "else", "to", "sort"]
>>> pairs = zip(list1, list2)
>>> pairs
[('what', 'something'), ("I'm", 'else'), ('sorting', 'to'), ('by', 'sort')]
>>> pairs.sort()
>>> result = [ x[1] for x in pairs ]
>>> result
['else', 'sort', 'to', 'something']
最後の段階の別のやり方は:
>>> result = []
>>> for p in pairs: result.append(p[1])
これのほうが読みやすいと、最後のリスト内包表記ではなくこれを使いたくなるかもしれません。しかし、これは長いリストではほぼ二倍の時間がかかります。なぜでしょうか。まず、append()
演算はメモリを割り当て直す必要があり、それを避けるために毎回ちょっと工夫していますが、それでも避けられないことがあるので、少し時間がかかるのです。二つ目に、"result.append" には属性探索が余計に必要で、三つ目に、これらすべての関数を呼ぶ必要があることで速度が落ちてしまいます。
オブジェクト¶
クラスとは何ですか?¶
クラスは、class 文の実行で生成される特殊なオブジェクトです。クラスオブジェクトはインスタンスオブジェクトを生成するためのテンプレートとして使われ、あるデータ型に特有のデータ (attribute/属性) とコード (メソッド) の両方を内蔵しています。
新しいクラスを一つ以上の他のクラス (新しいクラスの基底クラスと呼ばれます) に基づいて作ることもできます。この新しいクラスは、基底クラスから属性とメソッドを継承します。これにより、オブジェクトモデルを継承で連続的に洗練できます。メールボックスへの基本的なアクセサを提供する一般的な Mailbox
クラスを作って、それからいろいろな特定のメールボックスの形式を扱う MboxMailbox
、MaildirMailbox
、OutlookMailbox
のようなサブクラスを作れるのです。
メソッドとは何ですか?¶
メソッドは、オブジェクト x
が持つ関数で、通常 x.name(arguments...)
として呼び出されるものです。メソッドはクラス定義の中で関数として定義されます:
class C:
def meth(self, arg):
return arg * 2 + self.attribute
self とは何ですか?¶
self はメソッドの第一引数に慣習的につけられる名前にすぎません。meth(self, a, b, c)
として定義されたメソッドは、その定義がなされたクラスのインスタンス x
に対して x.meth(a, b, c)
として呼び出されます。呼び出されたメソッドは、meth(x, a, b, c)
が呼ばれたものと考えます。
なぜメソッドの定義や呼び出しにおいて 'self' を明示しなければならないのですか? も参照してください。
あるオブジェクトが、与えられたクラスやそのサブクラスのインスタンスであるかを調べるにはどうしますか?¶
ビルトイン関数 isinstance(obj, cls)
を使ってください。クラスのタプルを与えて isinstance(obj, (class1, class2, ...))
のようにすれば、あるオブジェクトが任意の数のクラスのオブジェクトであるかを調べられますし、 isinstance(obj, str)
や isinstance(obj, (int, long, float, complex))
のようにすれば、Python のビルトイン型のオブジェクトであるかも調べられます。
なお、大部分のプログラムでは、 isinstance()
をユーザー定義のクラスに何度も使うべきではありません。クラスを自分で開発するときに、適切なオブジェクト指向スタイルは、特定の振る舞いをカプセル化するクラスのメソッドを定義するものであって、オブジェクトのクラスを調べてそのクラスに応じて違うことをするものではありません。例えば、何かをする関数があったとして:
def search(obj):
if isinstance(obj, Mailbox):
... # code to search a mailbox
elif isinstance(obj, Document):
... # code to search a document
elif ...
よりよいアプローチは、search()
メソッドをすべてのクラスに定義して、それをただ呼び出すことです:
class Mailbox:
def search(self):
... # code to search a mailbox
class Document:
def search(self):
... # code to search a document
obj.search()
委譲とは何ですか?¶
委譲 (delegation) とは、オブジェクト指向のテクニック (デザインパターンとも呼ばれる) の一つです。オブジェクト x
があって、そのメソッドのうちただ一つの振る舞いを変えたいとしましょう。新しいクラスを作成し、変えたいメソッドだけを新しく実装し、他のすべてのメソッドを x
の対応するメソッドに委譲する新しいクラスを作れます。
Python プログラマは簡単に委譲を実装できます。例えば、以下のクラスは、ファイルのように振る舞いながらすべての文字を大文字に変換するクラスを実装します:
class UpperOut:
def __init__(self, outfile):
self._outfile = outfile
def write(self, s):
self._outfile.write(s.upper())
def __getattr__(self, name):
return getattr(self._outfile, name)
ここで UpperOut
クラスは write()
メソッドを定義しなおして、引数の文字列を大文字に変換してから基礎となる self.__outfile.write()
メソッドを呼び出すようにします。その他すべてのメソッドは基礎となる self.__outfile
オブジェクトに移譲されます。この委譲は __getattr__
メソッドを通してなされます。属性の制御の詳細は 言語リファレンス を参照してください。
なお、一般的に委譲はトリッキーになりがちです。属性が設定される時には読み出される時と同様に、そのクラスに __setattr__()
メソッドを定義する必要があり、それには細心の注意が必要です。 __setattr__()
の基本的な実装はおおよそ以下のようになります:
class X:
...
def __setattr__(self, name, value):
self.__dict__[name] = value
...
たいてい、 __setattr__()
実装は self.__dict__
を変更して、無限再帰を起こすことなくローカルな状態を保存するようにしなければなりません。
基底クラスで定義されたメソッドを、そのクラスをオーバーライドした派生クラスから呼び出すにはどうしますか?¶
新スタイルのクラスを使っている場合は、組み込みの super()
関数を使ってください:
class Derived(Base):
def meth(self):
super(Derived, self).meth()
旧スタイルクラスを使っているなら: class Derived(Base): ...
のようなクラス定義で、 Base.meth(self, arguments...)
とすれば、 Base
(または Base
の基底クラス) で定義された meth()
メソッドを呼び出せます。ここで、 Base.meth
は束縛されていないメソッドなので、 self
引数を渡す必要があります。
基底クラスの名前を変えやすいコードを書くにはどうしますか?¶
基底クラスのエイリアス (alias) を定義し、先にそれに本当の基底クラスを代入しておいてから、クラス定義の中でそのエイリアスを使うといいかもしれません。そうすればエイリアスに代入する値を変えるだけで済みます。ちなみに、この手法は使用する基底クラスを動的に選びたいとき、例えば使えるリソースによって選びたいときなどにも便利です。例:
BaseAlias = <real base class>
class Derived(BaseAlias):
def meth(self):
BaseAlias.meth(self)
...
静的なクラスデータや静的なクラスメソッドを作るにはどうしますか?¶
(C++ や Java の意味で) 静的なデータも静的なメソッドも Python でサポートされています。
静的なデータを作るには、単純にクラス属性を定義してください。その属性に新しい値を代入するには、代入するクラス名を明示する必要があります:
class C:
count = 0 # number of times C.__init__ called
def __init__(self):
C.count = C.count + 1
def getcount(self):
return C.count # or return self.count
c
そのものや c.__class__
から C
にいたるパス探索経路上のクラスによってオーバーライドされない限り、c.count
も isinstance(c, C)
であるすべての c
に対する C.count
を参照します。
注意: C のメソッド内では、self.count = 42
のような代入は self
自身の辞書に "count" という名前の新しくて関係ないインスタンスを作ります。クラスの静的なデータの再束縛には、メソッド内であるか否かにかかわらず、いつもクラスを指定しなければなりません:
C.count = 314
Python 2.2 からは、静的メソッドが使えます:
class C:
def static(arg1, arg2, arg3):
# No 'self' parameter!
...
static = staticmethod(static)
Python 2.4 のデコレータを使って、以下のようにも書けます:
class C:
@staticmethod
def static(arg1, arg2, arg3):
# No 'self' parameter!
...
しかし、静的メソッドの効果を得るもっと簡単な方法は、単にモジュールレベル関数を使うことです:
def getcount():
return C.count
モジュールあたりに一つのクラスを定義するように (あるいはクラス組織を厳密に関連させるように) コードが構成されているなら、これで必要なカプセル化ができます。
Python でコンストラクタ(やメソッド)をオーバーロードするにはどうしたらいいですか?¶
この質問の答えはすべてのメソッドについて言えることですが、この質問はだいたい以下の構造の文脈から出てきます。
C++ では、このように書けます
class C {
C() { cout << "No arguments\n"; }
C(int i) { cout << "Argument is " << i << "\n"; }
}
Python では、一つのコンストラクタでデフォルトの引数を使ってすべての場合に対応するように書かなければなりません。例えば:
class C:
def __init__(self, i=None):
if i is None:
print "No arguments"
else:
print "Argument is", i
これで完全に等価とは言えませんが、実用上は十分に近いです。
長さが変えられる引数のリストを試すには、例えば
def __init__(self, *args):
...
これと同じやり方がすべてのメソッド定義で使えます。
__spam を使おうとしたら _SomeClassName__spam からエラーがでました。¶
先頭にアンダースコアが二つ付いた変数名は、クラスのプライベートな変数を、"マングル化"という単純かつ効率のいい方法で定義します。__spam
のような形式 (先頭に二つ以上、末尾にもしあっても一つのアンダースコアがある) のすべての識別子は、classname
が先頭のアンダースコアをすべて削除した現在のクラス名とすれば、_classname__spam
のように文字上で置換えられます。
これはプライベートであることを保証するものではありません。これでも外部のユーザが "_classname__spam" 属性に直接アクセスできますし、プライベートな変数はオブジェクトの __dict__
から見えます。多くの Python プログラマはわざわざプライベートな変数名を使おうとなど考えません。
クラスに __del__ メソッドを定義しているのですが、オブジェクトを削除したときに呼ばれません。¶
いくつかの可能性があります。
del 文は必ずしも __del__()
を呼び出すとは限りません – これは単純にオブジェクトの参照カウントを減らすもので、カウントがゼロになったときに __del__()
が呼び出されます。
データ構造が循環リンク (子のそれぞれが親の参照を持ち、親のそれぞれが子のリストを持つツリーなど) を含む場合、その参照カウントは決して 0 にはなりません。時々、Python はこのようなサイクルを検出するアルゴリズムを実行しますが、データ構造への参照がなくなってからこのガベージコレクタが実行されるまでいくらか時間が掛かるかもしれないので、 __del__()
メソッドは不都合な予期できないタイミングで呼び出されるかもしれません。これは問題を再現しようとするときに不便です。さらに悪いことに、オブジェクトの __del__()
メソッドが実行される順序は任意です。 gc.collect()
を起動して収集を強制することができますが、オブジェクトが決して回収されないような本当に病的な場合も あります 。
周期的なコレクタにかかわらず、オブジェクトに close()
メソッドを明示的に定義し、使い終わったらいつでも呼び出せるようにするのはいいことです。そうすれば close()
メソッドはサブオブジェクトへの参照をする属性を取り除いてくれます。 __del__()
を直接呼び出さないでください – __del__()
は close()
を呼び出すでしょうし、 close()
なら同じオブジェクトに対して複数回呼ばれてもいいことが保証されているでしょう。
循環参照を避ける他の方法は、 weakref
モジュールを使って、参照カウントを増やすことなくオブジェクトを示すことです。例えばツリー構造は、親と (必要なら!) 兄弟に弱参照を使うべきです。
except 節で例外を捕まえた関数内でオブジェクトがローカル変数であったたなら、そのオブジェクトへの参照が関数のスタックフレーム内でスタックトレース内に含まれることで存在する可能性があります。通常、 sys.exc_clear()
を呼び出せば、最後に記録された例外を消去することで対処してくれます。
最後に、 __del__()
メソッドが例外を発生させた場合、警告のメッセージが sys.stderr
に書きこまれます。
与えられたクラスのすべてのインスタンスのリストを得るにはどうしますか?¶
Python はクラス (やビルトイン型) のすべてのインスタンスをたどりません。クラスのコンストラクタにそれぞれのインスタンスへの弱参照のリストを作らせることですべてのインスタンスをたどらせられます。
なぜ id()
の結果は一意でないように見えるのですか?¶
組み込みの id()
は、オブジェクトが生きている間は一意であることが保証されている整数値を返します。 CPython では、それはオブジェクトのメモリアドレスなので、オブジェクトがメモリから削除された後に、次に新しく生成されたオブジェクトはメモリの同じ場所にメモリ領域を確保されていることが、しばしば起きます。この現象を次の例で示しましょう:
>>> id(1000)
13901272
>>> id(2000)
13901272
2 つの同じ値を持つ id は id()
の実行の前に作られてすぐさま削除された異なる整数オブジェクトによるものです。id を調べたいオブジェクトがまだ生きてることを保証したいなら、オブジェクトへの別の参照を作ってください:
>>> a = 1000; b = 2000
>>> id(a)
13901272
>>> id(b)
13891296
モジュール¶
.pyc ファイルを作るにはどうしますか?¶
モジュールが最初にインポートされるとき (またはソースが現在コンパイルされているファイルよりも新しいとき)、コンパイルされたコードを含む .pyc
ファイルが .py
ファイルと同じディレクトリに作られるでしょう。
.pyc
ファイルが作られないとしたら、ディレクトリの権限の問題があるかもしれません。たとえばこれは、web サーバなどでテストするときのように、開発のときと違うユーザとして起動するときなどに起こりえます。モジュールをインポートしたときに Python がコンパイルされたモジュールをディレクトリに書き込むための条件 (権限、容量の空き、etc…) が揃っていれば、.pyc ファイルの生成は自動的に行われます。
トップレベルのスクリプトを実行することはインポートとは見做されず、 .pyc
は生成されません。例えば、トップレベルモジュール abc.py
があって、他のモジュール xyz.py
をインポートするようになっているとき、abc を起動すると、xyz のインポート時に xyz.pyc
が生成されますが、 abc.py
はインポートされないので abc.pyc
ファイルは生成されません。
abc.pyc が必要なら – つまり、インポートされないモジュールの .pyc
ファイルを生成するためには – py_compile
や compileall
モジュールが利用できます。
py_compile
モジュールは手動で任意のモジュールをコンパイルできます。やり方の一つは、このモジュールの compile()
関数をインタラクティブに実行することです:
>>> import py_compile
>>> py_compile.compile('foo.py')
このように実行すると、foo.py
と同じ場所に .pyc
が書き出されます (出力ファイルの位置は、オプション引数 cfile
で上書きすることもできます)。
compileall
モジュールを使えば自動的に一つや複数のディレクトリのすべてのファイルをコンパイルできます。シェルプロンプトから compileall.py
を起動して、コンパイルしたいファイルを含むディレクトリのパスを指定してください:
python -m compileall .
現在のモジュール名を知るにはどうしますか?¶
モジュールは前もって定義されたグローバル変数 __name__
を検索することで自身の名前を決定できます。この値が '__main__'
であるとき、そのプログラムはスクリプトとして実行されています。インポートされることによって使われる大抵のモジュールはコマンドラインインタフェースや自己テストも提供していて、__name__
をチェックしてからそのコードだけを実行します:
def main():
print 'Running test...'
...
if __name__ == '__main__':
main()
相互にインポートしあうモジュールを作るにはどうしたらいいですか?¶
以下のモジュールがあったとしましょう:
foo.py:
from bar import bar_var
foo_var = 1
bar.py:
from foo import foo_var
bar_var = 2
問題はインタプリタが以下の段階を実行することです:
- main が foo をインポートする
- foo の空のグローバルが生成される
- foo がコンパイルされ実行を始める
- foo が bar をインポートする
- bar の空のグローバルが生成される
- bar がコンパイルされ実行を始める
- bar が foo をインポートする(すでに foo という名前のモジュールがあるので no-op となる)
- bar.foo_var = foo.foo_var
この最後の段階は失敗します。Python が foo
を解釈し終わっていなくて、foo
のグローバルなシンボルの辞書はまだ空ですから。
import foo
を使って、グローバルコードの foo.foo_var
にアクセスしようとしたときにも、これと同じことが起こります。
この問題には (少なくとも) 三つの解決策があります。
Guido van Rossum は from <module> import ...
を全く使わないで、すべてのコードを関数の中に入れることを勧めています。グローバル変数とクラス変数の初期化は定数とビルトイン関数のみで行われるべきです。これでインポートされたすべてのモジュールは <module>.<name>
として参照されることになります。
Jim Roskind はそれぞれのモジュールに対して以下の順に進めることを提案しています:
- エクスポート (インポートされた基底クラスを必要としないグローバル、関数、クラス)
import
文- アクティブなコード (インポートされた値によって初期化されるグローバルを含む)。
インポートが奇妙な場所に現れることから van Rossum はこの方法をそれほど好みませんが、これは有効です。
Matthias Urlichs は第一に再帰インポートが必要ないようにコードを構築しなおすことを推奨しています。
これらの解決策はそれぞれ両立させることもできます。
__import__('x.y.z') は <module 'x'> を返しますが、z を得るためにはどうしますか?¶
mod:importlib に import_module()
という便利な関数があるので、代わりにそちらを使用することを検討してください。
z = importlib.import_module('x.y.z')
インポートされたモジュールを編集してから再インポートしましたが、変化が現れません。なぜですか?¶
効率と一貫性上の理由から、Python はモジュールが最初にインポートされた時にのみモジュールファイルを読み込みます。そうしないと、たくさんのモジュールでできていて、それぞれが同じ基本モジュールをインポートしているようなプログラムでは、その基本モジュールの解析と再解析が繰り返されることになります。変更されさたモジュールの再読込を強制するには、こうしてください:
import modname
reload(modname)
注意:この手法は 100%安全とは言えません。とりわけ
from modname import some_objects
のような文を含むモジュールは、インポートされたオブジェクトの古いバージョンを使い続けます。そのモジュールにクラス定義が含まれていたら、存在するクラスインスタンスは新しいクラス定義を使うようにアップデート されません。これによって以下の矛盾した振舞いがなされえます:
>>> import cls
>>> c = cls.C() # Create an instance of C
>>> reload(cls)
<module 'cls' from 'cls.pyc'>
>>> isinstance(c, cls.C) # isinstance is false?!?
False
この問題の本質は、クラスオブジェクトを印字することで明らかになります:
>>> c.__class__
<class cls.C at 0x7352a0>
>>> cls.C
<class cls.C at 0x4198d0>