プログラミング FAQ¶
目次
- プログラミング FAQ
- 一般的な質問
- コア言語
- なぜ変数に値があるのに UnboundLocalError が出るのですか?
- Python のローカルとグローバル変数のルールは何ですか?
- ループの中で異なる値で定義されたラムダ式が、同じ値を返すのはなぜですか?
- グローバル変数をモジュール間で共有するにはどうしたらいいですか?
- モジュールで import を使う際の「ベストプラクティス」は何ですか?
- なぜオブジェクト間でデフォルト値が共有されるのですか?
- オプションパラメータやキーワードパラメータを関数から関数へ渡すにはどうしたらいいですか?
- 実引数と仮引数の違いは何ですか?
- なぜ list 'y' を変更すると list 'x' も変更されるのですか?
- 出力引数のある関数 (参照渡し) はどのように書きますか?
- Python で高次関数はどのようにつくりますか?
- Python のオブジェクトはどのようにコピーしますか?
- オブジェクトのメソッドや属性はどのように見つけますか?
- コードはどのようにオブジェクトの名前を見つけるのですか?
- カンマ演算子はなぜ優先されるのですか?
- C の "?:" 三項演算子と等価なものはありますか?
- Python で解し難いワンライナーを書くことはできますか?
- 数と文字列
- 性能
- シーケンス(タプル/リスト)
- 辞書型 (dictionary)
- オブジェクト
- クラスとは何ですか?
- メソッドとは何ですか?
- self とは何ですか?
- あるオブジェクトが、与えられたクラスやそのサブクラスのインスタンスであるかを調べるにはどうしますか?
- 委譲とは何ですか?
- 基底クラスで定義されたメソッドを、そのクラスをオーバーライドした派生クラスから呼び出すにはどうしますか?
- 基底クラスの名前を変えやすいコードを書くにはどうしますか?
- 静的なクラスデータや静的なクラスメソッドを作るにはどうしますか?
- Python でコンストラクタ(やメソッド)をオーバーロードするにはどうしたらいいですか?
- __spam を使おうとしたら _SomeClassName__spam からエラーがでました。
- クラスに __del__ メソッドを定義しているのですが、オブジェクトを削除したときに呼ばれません。
- 与えられたクラスのすべてのインスタンスのリストを得るにはどうしますか?
- なぜ
id()
の結果は一意でないように見えるのですか?
- モジュール (module)
一般的な質問¶
ブレークポイントやシングルステップ実行などを備えたソースコードレベルデバッガはありますか?¶
はい。
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 配列に変換します。すべてのモジュールを新しいプログラムに埋め込む C コンパイラで、そのプログラムは Python モジュールにリンクされます。
これはあなたのソースの (両方の形式の) import 文を再帰的にスキャンし、import されたモジュールを標準の Python パスと (組み込みモジュールのある) ソースディレクトリから探します。そして Python で書かれたモジュールのバイトコードを C コード (marshal モジュールでコードオブジェクトに変換できる配列) に変換し、実際にそのプログラム内で使われている組み込みモジュールだけが含まれたカスタムメイドの設定ファイルを作成します。そして生成された C コードをコンパイルして Python インタプリタの残りとリンクし、元のスクリプトと全く同じように動作する自己完結的なバイナリを形成します。
もちろん、freeze には C コンパイラが必要です。C コンパイラを必要としない選択肢もあります。その一つは、Thomas Heller の py2exe (Windows 専用) です
他には Anthony Tuininga の cx_Freeze があります。
Python プログラムのためのコーディングスタンダードやスタイルガイドはありますか?¶
はい。標準ライブラリモジュールに求められるコーディングスタイルは PEP 8 として文書化されています。
コア言語¶
なぜ変数に値があるのに 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
同様のことを、ネストされたスコープで nonlocal
予約語を使うことでもできます:
>>> def foo():
... x = 10
... def bar():
... nonlocal x
... print(x)
... x += 1
... bar()
... print(x)
>>> foo()
10
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、など
- 自前で開発したモジュール
循環参照の問題を避けるために、インポートを関数やクラスに移すことが必要なときもあります。Gordon McMillan によれば:
循環参照は両方のモジュールが "import <module>" 形式のインポートを使っていれば大丈夫です。二つ目のモジュールが最初のモジュールから名前を確保しようとして ("from module import name")、そのインポートがトップレベルにあると駄目です。最初のモジュールが二つ目のモジュールをインポートするのに忙しくて、最初のモジュールの名前が利用可能になっていないからです。
この状況では、二つ目のモジュールが一つの関数の中でのみ使われているならば、そのインポートは簡単に関数の中に移せます。インポートが呼ばれたとき、最初のモジュールは初期化を完了していて、二つ目のモジュールは自分のインポートをできます。
プラットフォーム依存のモジュールがあるときには、インポートをトップレベルの外に動かすことも必要です。この場合、ファイルの先頭ではすべてのモジュールをインポートすることさえできないかもしれません。この場合は、対応するプラットフォームに合わせたコードで正しいモジュールをインポートすることを選ぶと良いです。
循環参照の問題を避けたりモジュールの初期化にかかる時間を減らしたりしたいなら、単にインポートを関数定義の中などのローカルなスコープに移してください。この手法は多くのインポートがプログラムがどのように実行されるかに依存しなくてよいときに特に有効です。ある関数の中でのみモジュールが使われるのなら、インポートをその関数の中に移すことを考えてもいいでしょう。なお、モジュールを読み込む最初の回はモジュールの初期化の時間のために高価になりえますが、複数回目にモジュールを読み込むのは事実上無料、辞書探索の数回のコストだけで済みます。モジュール名がスコープから外れてさえ、そのモジュールはおそらく sys.modules
から利用できるでしょう。
オプションパラメータやキーワードパラメータを関数から関数へ渡すにはどうしたらいいですか?¶
関数のパラメータリストに引数を *
と **
指定子 (specifier) で集めてください。そうすれば、位置引数をタプルとして、キーワード引数を辞書として得られます。これで、他の関数を呼び出すときに *
と **
を使ってそれらの引数を渡せます:
def f(x, *args, **kwargs):
...
kwargs['width'] = '14.3c'
...
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
は新しいオブジェクトを作ります)。
言い換えると:
出力引数のある関数 (参照渡し) はどのように書きますか?¶
前提として、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 object at 0x16D07CC>
>>> print(a)
<__main__.A object 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 の "?:" 三項演算子と等価なものはありますか?¶
はい、あります。構文は以下のようになります:
[on_true] if [expression] else [on_false]
x, y = 50, 25
small = x if x < y else y
この構文が導入された 2.5 以前のバージョンに関しては、論理演算子を使ったこのイディオムが一般的でした:
[expression] and [on_true] or [on_false]
しかし、このイディオムは安全ではありません。on_true のブール値が偽であるときに間違った結果を与えることがあります。ですから、いつでも ... if ... else ...
形式を使ったほうが良いです。
Python で解し難いワンライナーを書くことはできますか?¶
はい。そういうものはたいてい、 lambda
の中に lambda
がネストされています。Ulf Bartelt による下の3つの例を見てください:
from functools import reduce
# Primes < 1000
print(list(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(list(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
は噛み付きかねないバグです。
文字列を数に変換するにはどうしたらいいですか?¶
整数に変換するには、組み込みの int()
型コンストラクタを使ってください。例えば、 int('144') == 144
です。同様に、 float()
は浮動小数点に変換します。例えば、 float('144') == 144.0
です。
デフォルトでは、これらは数を十進数として解釈するので、 int('0144') == 144
や 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' 以外の) 十進数を '0' で始めてはならないからです。
数を文字列に変換するにはどうしたらいいですか?¶
例えば、144 という数を '144' という文字列に変換したいなら、組み込み型のコンストラクタ str()
を使ってください。
十六進数や八進数にしたければ、組み込み関数の hex()
や oct()
を使ってください。
手の込んだフォーマット形式を使うなら、 フォーマット済み文字列リテラル と 書式指定文字列の文法 の節を参照してください。
例えば、 "{:04d}".format(144)
は '0144'
になり、 "{:.3f}".format(1.0/3.0)
は '0.333'
になります。
文字列をインプレースに変更するにはどうしたらいいですか?¶
文字列はイミュータブルなので、それはできません。殆どの場合、組み立てたい個別の部品から単純に新しい文字列を構成するべきです。しかし、Unicode データをインプレースに変更できるオブジェクトが必要なら、 array
モジュールの io.StringIO
オブジェクトを試してください:
>>> import io
>>> s = "Hello, world"
>>> sio = io.StringIO(s)
>>> sio.getvalue()
'Hello, world'
>>> sio.seek(7)
7
>>> sio.write("there!")
6
>>> sio.getvalue()
'Hello, there!'
>>> import array
>>> a = array.array('u', s)
>>> print(a)
array('u', 'Hello, world')
>>> a[0] = 'y'
>>> print(a)
array('u', 'yello, world')
>>> a.tounicode()
'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() に相当するものはありますか?¶
S.rstrip("\r\n")
を使って文字列 S
の終端から他の空白文字を取り除くことなくすべての行末記号を取り除くことができます。文字列 S
が複数行を表し、終端に空行があるとき、そのすべての空行も取り除かれます:
>>> lines = ("line 1 \r\n"
... "\r\n"
... "\r\n")
>>> lines.rstrip("\n\r")
'line 1 '
これは典型的に一度に一行ずつテキストを読みたい時にのみ使われるので、S.rstrip()
をこの方法で使うとうまくいきます。
scanf() や sscanf() と同等なものはありますか?¶
そのようなものはありません。
簡単な入力解析で、多くの場合に一番簡単な方法は、文字列オブジェクトの split()
メソッドで行を空白文字で区切られた単語に分け、十進数の文字列を int()
や float()
で数値に変換することです。 split()
にはオプションの "sep" 変数があり、行に空白文字以外の区切りを使っているときに便利です。
もっと複雑な入力解析をしたいなら、C の sscanf()
よりも正規表現の方が便利ですし、この処理に向いています。
性能¶
プログラムが遅すぎます。どうしたら速くなりますか?¶
これは、一般的に難しい問題です。まず、先に進む前に覚えておいて欲しいことをここに挙げます:
- 性能の傾向は Python 実装によって変わります。この FAQ では CPython に焦点を当てます。
- 振る舞いはオペレーティングシステムによって変わりえます。特に、I/O やマルチスレッドに関しては顕著です。
- 常に、コードの最適化を始める 前に プログラムのホットスポットを見つけるべきです (
profile
モジュールを参照してください)。 - ベンチマークスクリプトを書くことで、改善箇所の捜索を素早く繰り返せます (
timeit
モジュールを参照してください)。 - 洗練された最適化に隠れたリグレッションの可能性を生む前に、(ユニットテストやその他の技法で) コードカバレッジを上げることを強く推奨します。
とは言っても、Python コードを高速化する技法はたくさんあります。ここでは、満足な性能のレベルにたどり着くまでの長い道のりを進む、一般的な方針を示します:
- コード中に細かい最適化の技法をばらまこうとするよりも、アルゴリズムを高速化 (または高速なアルゴリズムに変更) するほうが、大きな利益を生むことがあります。
- 適切なデータ構造を使ってください。組み込み型 や
collections
を調べてください。 - 何かをするための基本要素が標準ライブラリにあるなら、自分で発明した代用品よりもそちらのほうが、(絶対にとは言えませんが) おそらく速いです。
それが組み込み型やある種の拡張型のように C で書かれたものならなおさらです。
たとえば、ソートするには、必ず
list.sort()
組み込みメソッドかsorted()
関数を使ってください (また、中程度に高度な例は、 ソート HOW TO を参照してください)。 - 抽象化は、遠回りにしがちで、インタプリタの作業を増やすことになります。この遠回りさが、なされる作業の量より重大になると、プログラムが遅くなってしまいます。過度な抽象化、特に細かい関数やメソッドの形で現れるもの (これは読みにくさも落とします) は防ぐべきです。
pure Python にできる限界に達したなら、更に進むためのツールがあります。例えば、 Cython は、Python コードのわずかに変形した版を C 拡張にコンパイルし、多種のプラットフォームで使えます。Cython は、コンパイル (と任意の型アノテーション) を利用し、コードの解釈を大幅に速くします。C プログラミングに自信があるなら、自分で write a C extension module こともできます。
参考
パフォーマンス tips が載っている wiki のページ。
多くの文字列を結合するのに最も効率的な方法は何ですか?¶
str
および bytes
オブジェクトはイミュータブルなので、多くの文字列の結合は結合ごとに新しいオブジェクトを作成し、効率が悪いです。
一般的に、全体の実行時間のコストは文字列の長さの二乗に比例します。
多くの str
オブジェクトを累積するのにおすすめのイディオムは、すべてをリストに配置してから最後に str.join()
を呼び出すことです:
chunks = []
for s in my_strings:
chunks.append(s)
result = ''.join(chunks)
(他の割と効率的なイディオムは、 io.StringIO
を使うことです)
多くの bytes
オブジェクトを累積するのにおすすめのイディオムは、 bytearray
オブジェクトをインプレース結合 (+=
演算子) で拡張することです:
result = bytearray()
for b in my_bytes_objects:
result += b
シーケンス(タプル/リスト)¶
タプル、リスト間の変更はどのようにするのですか?¶
型コンストラクタ 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) なら、おそらくこのほうが速いです
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 が最もよく知られています。
なぜ加算はされるのに 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]
が既に指しているオブジェクトと同じオブジェクトを指していたとしても、タプルは不変なので、その最後の代入はやはりエラーとなります。
辞書型 (dictionary)¶
複雑なソートがしたいのですが、Python でシュワルツ変換はできますか?¶
Perl コミュニティの Randal Schwartz の作とされるこのテクニックは、リストの要素を、それぞれの要素をその「ソート値」に対応付けるメトリックによってソートします。Python では、 list.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 = sorted(pairs)
>>> pairs
[("I'm", 'else'), ('by', 'sort'), ('sorting', 'to'), ('what', 'something')]
>>> 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, 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()
3.0 以前のバージョンで旧スタイルクラスを使っているなら: 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
静的メソッドが使えます:
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
モジュールを使って、参照カウントを増やすことなくオブジェクトを示すことです。例えばツリー構造は、親と (必要なら!) 兄弟に弱参照を使うべきです。
最後に、 __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
モジュール (module)¶
.pyc ファイルを作るにはどうしますか?¶
モジュールが初めてインポートされたとき (もしくは、現在のコンパイルされたファイルが作られてから、ソースファイルが変更されたとき) 、コンパイルされたコードが入っている .pyc
ファイルが、 .py
ファイルのあるディレクトリのサブディレクトリ __pycache__
に作成されます。 .pyc
ファイルのファイル名は、 .py
ファイルの名前で始まり、 .pyc
で終わり、中間部分はこのファイルを作った python
バイナリに依存した文字列になります。 (詳細は PEP 3147 を参照してください。)
.pyc
が作られない理由の 1 つは、ソースファイルがあるディレクトリの権限の問題、つまり __pycache__
サブディレクトリが作れない問題です。これは、例えば、ウェブサーバーでテストを行っているときのような、開発者のユーザと実行者のユーザが別な場合に、起こり得ます。
PYTHONDONTWRITEBYTECODE
環境変数がセットされない限り、モジュールをインポートしていて、 Python に __pycache__
サブディレクトリを作り、そこにコンパイルされたモジュールが置ける能力 (権限、ディスクの空きスペース、など) がある場合は、 .pyc ファイルは自動的に作られます。
最上位のスクリプトを Python で実行するのはインポートとはみなされず、.pyc
は作成されません。例えば、最上位のモジュール foo.py
が別のモジュール xyz.py
をインポートしている場合、(シェルコマンドとして python foo.py
と打ち込んで) foo
を実行すると、xyz
はインポートされるので xyz
の .pyc
は作成されますが、foo.py
はインポートされたわけではないので foo
の .pyc
は作られません。
foo
の .pyc
ファイルを作成する – つまり、インポートされていないモジュールの .pyc
ファイルを作成する – 必要がある場合、 py_compile
モジュールと compileall
モジュールを使えば可能です。
py_compile
モジュールは手動で任意のモジュールをコンパイルできます。やり方の一つは、このモジュールの compile()
関数をインタラクティブに実行することです:
>>> import py_compile
>>> py_compile.compile('foo.py')
このように実行すると、foo.py
と同じ場所の __pycache__
サブディレクトリに .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 を得るためにはどうしますか?¶
importlib
に import_module()
という便利な関数があるので、代わりにそちらを使用することを検討してください。
z = importlib.import_module('x.y.z')
インポートされたモジュールを編集してから再インポートしましたが、変化が現れません。なぜですか?¶
効率と一貫性上の理由から、Python はモジュールが最初にインポートされた時にのみモジュールファイルを読み込みます。そうしないと、たくさんのモジュールでできていて、それぞれが同じ基本モジュールをインポートしているようなプログラムでは、その基本モジュールの解析と再解析が繰り返されることになります。変更されさたモジュールの再読込を強制するには、こうしてください:
import importlib
import modname
importlib.reload(modname)
注意:この手法は 100%安全とは言えません。とりわけ
from modname import some_objects
のような文を含むモジュールは、インポートされたオブジェクトの古いバージョンを使い続けます。そのモジュールにクラス定義が含まれていたら、存在するクラスインスタンスは新しいクラス定義を使うようにアップデート されません。これによって以下の矛盾した振舞いがなされえます:
>>> import importlib
>>> import cls
>>> c = cls.C() # Create an instance of C
>>> importlib.reload(cls)
<module 'cls' from 'cls.py'>
>>> isinstance(c, cls.C) # isinstance is false?!?
False
この問題の本質は、クラスオブジェクトの "同一性" を出力することで明らかになります:
>>> hex(id(c.__class__))
'0x7352a0'
>>> hex(id(cls.C))
'0x4198d0'