プログラミングに関するFAQ
コンテンツ
- プログラミングに関するFAQ
- 一般的な質問
- コア言語
- 変数に値があるのにUnboundLocalErrorが発生するのはなぜですか?
- Pythonのローカル変数とグローバル変数のルールは何ですか?
- 異なる値を持つループで定義されたラムダがすべて同じ結果を返すのはなぜですか?
- モジュール間でグローバル変数を共有するにはどうすればよいですか?
- モジュールでインポートを使用するための「ベストプラクティス」は何ですか?
- デフォルト値がオブジェクト間で共有されるのはなぜですか?
- オプションまたはキーワードパラメータをある関数から別の関数に渡すにはどうすればよいですか?
- 引数とパラメータの違いは何ですか?
- リスト「y」を変更するとリスト「x」も変更されたのはなぜですか?
- 出力パラメーター(参照による呼び出し)を使用して関数を作成するにはどうすればよいですか?
- Pythonで高階関数を作成するにはどうすればよいですか?
- Pythonでオブジェクトをコピーするにはどうすればよいですか?
- オブジェクトのメソッドまたは属性を見つけるにはどうすればよいですか?
- コードはどのようにしてオブジェクトの名前を検出できますか?
- コンマ演算子の優先順位はどうなっていますか?
- Cの「?:」三項演算子に相当するものはありますか?
- 難読化されたワンライナーをPythonで書くことは可能ですか?
- 関数のパラメータリストのスラッシュ(/)はどういう意味ですか?
- 数字と文字列
- パフォーマンス
- シーケンス(タプル/リスト)
- タプルとリストを変換するにはどうすればよいですか?
- 負の指数とは何ですか?
- シーケンスを逆の順序で繰り返すにはどうすればよいですか?
- リストから重複を削除するにはどうすればよいですか?
- リストから複数のアイテムを削除するにはどうすればよいですか
- Pythonでどのように配列を作成しますか?
- 多次元リストを作成するにはどうすればよいですか?
- 一連のオブジェクトにメソッドを適用するにはどうすればよいですか?
- 追加が機能するときにa_tuple [i] + = ['item']が例外を発生させるのはなぜですか?
- 複雑な並べ替えをしたいのですが、Pythonでシュワルツ変換を実行できますか?
- あるリストを別のリストの値で並べ替えるにはどうすればよいですか?
- オブジェクト
- クラスとは何ですか?
- 方法とは何ですか?
- 自己とは何ですか?
- オブジェクトが特定のクラスのインスタンスであるか、そのサブクラスのインスタンスであるかを確認するにはどうすればよいですか?
- 委任とは何ですか?
- 基本クラスで定義されたメソッドを、それをオーバーライドする派生クラスから呼び出すにはどうすればよいですか?
- 基本クラスを簡単に変更できるようにコードを整理するにはどうすればよいですか?
- 静的クラスデータと静的クラスメソッドを作成するにはどうすればよいですか?
- Pythonでコンストラクター(またはメソッド)をオーバーロードするにはどうすればよいですか?
- __spamを使おうとすると、_SomeClassName__spamに関するエラーが発生します。
- 私のクラスは__del__を定義していますが、オブジェクトを削除しても呼び出されません。
- 特定のクラスのすべてのインスタンスのリストを取得するにはどうすればよいですか?
id()
の結果が一意ではないように見えるのはなぜですか?
- モジュール
一般的な質問
ブレークポイント、シングルステップなどを備えたソースコードレベルのデバッガーはありますか?
はい。
Python用のいくつかのデバッガーについて以下に説明します。組み込み関数 breakpoint()を使用すると、それらのいずれかにドロップできます。
pdbモジュールは、Python用のシンプルですが適切なコンソールモードデバッガーです。 これは標準のPythonライブラリの一部であり、ライブラリリファレンスマニュアルに記載されています。 例としてpdbのコードを使用して、独自のデバッガーを作成することもできます。
標準のPythonディストリビューション(通常はツール/スクリプト/アイドルとして利用可能)の一部であるIDLEインタラクティブ開発環境には、グラフィカルデバッガーが含まれています。
PythonWinは、pdbに基づくGUIデバッガーを含むPythonIDEです。 PythonWinデバッガーはブレークポイントに色を付け、PythonWin以外のプログラムのデバッグなどの優れた機能を多数備えています。 PythonWinは、 pywin32 プロジェクトの一部および ActivePython ディストリビューションの一部として利用できます。
Eric は、PyQtとScintilla編集コンポーネント上に構築されたIDEです。
trepan3k はgdbのようなデバッガーです。
Visual Studio Code は、バージョン管理ソフトウェアと統合するデバッグツールを備えたIDEです。
グラフィカルデバッガーを含む多くの商用PythonIDEがあります。 それらが含まれます:
バグの発見や静的分析の実行に役立つツールはありますか?
はい。
Pylint と Pyflakes は、バグをより早く発見するのに役立つ基本的なチェックを行います。
Mypy 、 Pyre 、 Pytype などの静的型チェッカーは、Pythonソースコードの型ヒントをチェックできます。
Pythonスクリプトからスタンドアロンバイナリを作成するにはどうすればよいですか?
ユーザーが最初にPythonディストリビューションをインストールしなくてもダウンロードして実行できるスタンドアロンプログラムだけが必要な場合は、PythonをCコードにコンパイルする機能は必要ありません。 プログラムに必要なモジュールのセットを決定し、これらのモジュールをPythonバイナリとバインドして、単一の実行可能ファイルを生成するツールは多数あります。
1つは、PythonソースツリーにTools/freeze
として含まれているフリーズツールを使用することです。 PythonバイトコードをC配列に変換します。 Cコンパイラを使用すると、すべてのモジュールを新しいプログラムに埋め込んで、標準のPythonモジュールにリンクすることができます。
これは、ソースを再帰的にスキャンしてインポートステートメント(両方の形式)を探し、標準のPythonパスとソースディレクトリ(組み込みモジュールの場合)でモジュールを探すことで機能します。 次に、Pythonで記述されたモジュールのバイトコードをCコード(マーシャルモジュールを使用してコードオブジェクトに変換できる配列初期化子)に変換し、実際に使用される組み込みモジュールのみを含むカスタムメイドの構成ファイルを作成します。プログラム。 次に、生成されたCコードをコンパイルし、それをPythonインタープリターの残りの部分とリンクして、スクリプトとまったく同じように機能する自己完結型のバイナリを形成します。
明らかに、フリーズにはCコンパイラが必要です。 そうでない他のいくつかのユーティリティがあります:
コア言語
変数に値があるのに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
がループ内のその時点で持っていた値と同じになります。 。 つまり、n
の値は、最初のラムダでは0
、2番目のラムダでは1
、3番目のラムダでは2
というようになります。 したがって、各ラムダは正しい結果を返すようになりました。
>>> squares[2]()
4
>>> squares[4]()
16
この動作はラムダに固有のものではなく、通常の関数にも適用されることに注意してください。
モジュールでインポートを使用するための「ベストプラクティス」は何ですか?
通常、from modulename import *
は使用しないでください。 そうすることで、インポーターの名前空間が乱雑になり、リンターが未定義の名前を検出するのがはるかに困難になります。
ファイルの先頭にあるモジュールをインポートします。 そうすることで、コードに必要な他のモジュールが明確になり、モジュール名がスコープ内にあるかどうかの質問が回避されます。 1行に1つのインポートを使用すると、モジュールインポートの追加と削除が簡単になりますが、1行に複数のインポートを使用すると、使用する画面スペースが少なくなります。
次の順序でモジュールをインポートすることをお勧めします。
- 標準ライブラリモジュール–例
sys
、os
、getopt
、re
- サードパーティのライブラリモジュール(Pythonのsite-packagesディレクトリにインストールされているもの)–例 mx.DateTime、ZODB、PIL.Imageなど。
- ローカルで開発されたモジュール
循環インポートの問題を回避するために、インポートを関数またはクラスに移動する必要がある場合があります。 ゴードンマクミランは言う:
両方のモジュールが「インポート」を使用する場合、循環インポートは問題ありません。 」形式のインポート。 2番目のモジュールが最初のモジュールから名前を取得する必要があり(「モジュールのインポート名から」)、インポートが最上位にある場合、これらは失敗します。 これは、最初のモジュールが2番目のモジュールのインポートでビジー状態であるため、1番目の名前がまだ使用できないためです。
この場合、2番目のモジュールが1つの関数でのみ使用されると、インポートをその関数に簡単に移動できます。 インポートが呼び出されるまでに、最初のモジュールは初期化を完了し、2番目のモジュールはそのインポートを実行できます。
一部のモジュールがプラットフォーム固有である場合は、インポートをコードのトップレベルから移動する必要がある場合もあります。 その場合、ファイルの先頭にあるすべてのモジュールをインポートすることさえできない場合があります。 この場合、対応するプラットフォーム固有のコードに正しいモジュールをインポートすることをお勧めします。
循環インポートの回避などの問題を解決する必要がある場合、またはモジュールの初期化時間を短縮しようとしている場合にのみ、インポートを関数定義内などのローカルスコープに移動します。 この手法は、プログラムの実行方法によってはインポートの多くが不要な場合に特に役立ちます。 モジュールがその関数でのみ使用される場合は、インポートを関数に移動することもできます。 モジュールの初期化は1回であるため、モジュールを最初にロードするのはコストがかかる場合がありますが、モジュールを複数回ロードするのは事実上無料であり、辞書を数回検索するだけで済みます。 モジュール名がスコープ外になった場合でも、モジュールはおそらく sys.modules で入手できます。
オプションまたはキーワードパラメータをある関数から別の関数に渡すにはどうすればよいですか?
関数のパラメーターリストの*
および**
指定子を使用して引数を収集します。 これにより、位置引数がタプルとして、キーワード引数が辞書として提供されます。 次に、*
および**
を使用して、別の関数を呼び出すときにこれらの引数を渡すことができます。
def f(x, *args, **kwargs):
...
kwargs['width'] = '14.3c'
...
g(x, *args, **kwargs)
引数とパラメータの違いは何ですか?
パラメータは関数定義に表示される名前で定義されますが、引数は関数を呼び出すときに実際に関数に渡される値です。 パラメータは、関数が受け入れることができる引数のタイプを定義します。 たとえば、関数の定義が与えられた場合:
def func(foo, bar=None, **kwargs):
pass
foo 、 bar 、および kwargs は、func
のパラメーターです。 ただし、func
を呼び出す場合は、次のようになります。
func(42, bar=314, extra=somevar)
値42
、314
、およびsomevar
は引数です。
リスト「y」を変更するとリスト「x」も変更されたのはなぜですか?
次のようなコードを記述した場合:
>>> x = []
>>> y = x
>>> y.append(10)
>>> y
[10]
>>> x
[10]
y
に要素を追加するとx
も変更されたのはなぜか疑問に思われるかもしれません。
この結果を生み出す2つの要因があります。
- 変数は、オブジェクトを参照する単なる名前です。
y = x
を実行しても、リストのコピーは作成されません。x
が参照するのと同じオブジェクトを参照する新しい変数y
が作成されます。 これは、オブジェクト(リスト)が1つだけであり、x
とy
の両方がそれを参照していることを意味します。 - リストは可変です。つまり、リストの内容を変更できます。
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
はもはや等しくないことがわかります。 これは、整数が不変であり、x = x + 1
を実行するときに、その値をインクリメントすることによってint 5
を変更しないためです。 代わりに、新しいオブジェクト(int 6
)を作成し、それをx
に割り当てます(つまり、x
が参照するオブジェクトを変更します)。 この割り当ての後、2つのオブジェクト(int 6
と5
)とそれらを参照する2つの変数(x
は6
を参照しますがy
は引き続き5
を参照します。
一部の操作(y.append(10)
やy.sort()
など)はオブジェクトを変更しますが、表面的には同様の操作(y = y + [10]
やsorted(y)
など)は新しいオブジェクトを作成します。 一般に、Python(およびすべての場合で標準ライブラリ)では、オブジェクトを変更するメソッドはNone
を返し、2つのタイプの操作が混同されないようにします。 したがって、y.sort()
を誤って記述した場合、y
のソートされたコピーが得られると考えて、代わりにNone
になり、プログラムが生成する可能性があります。簡単に診断できるエラー。
ただし、同じ操作が異なるタイプで異なる動作をすることがある1つのクラスの操作があります。それは、拡張代入演算子です。 たとえば、+=
はリストを変更しますが、タプルやintは変更しません(a_list += [1, 2, 3]
はa_list.extend([1, 2, 3])
と同等であり、a_list
を変更しますが、some_tuple += (1, 2, 3)
と[ X143X] 新しいオブジェクトを作成します)。
言い換えると:
- 可変オブジェクト( list 、 dict 、 set など)がある場合、特定の操作を使用して、オブジェクトとそれを参照するすべての変数を変更できます。それに変化が見られます。
- 不変オブジェクト( str 、 int 、 tuple など)がある場合、それを参照するすべての変数は常に同じ値を参照しますが、その値を新しい値に変換する操作は、常に新しいオブジェクトを返します。
2つの変数が同じオブジェクトを参照しているかどうかを知りたい場合は、 is 演算子、または組み込み関数 id()を使用できます。
出力パラメーター(参照による呼び出し)を使用して関数を作成するにはどうすればよいですか?
引数はPythonの代入によって渡されることに注意してください。 割り当てはオブジェクトへの参照を作成するだけなので、呼び出し元と呼び出し先の引数名の間にエイリアスはなく、参照による呼び出し自体はありません。 さまざまな方法で目的の効果を実現できます。
結果のタプルを返すことによって:
>>> def func1(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 >>> func1(x, y) ('new-value', 100)
これはほとんどの場合、最も明確な解決策です。
グローバル変数を使用する。 これはスレッドセーフではないため、お勧めしません。
変更可能な(インプレースで変更可能な)オブジェクトを渡すことにより、次のようになります。
>>> def func2(a): ... a[0] = 'new-value' # 'a' references a mutable list ... a[1] = a[1] + 1 # changes a shared object ... >>> args = ['old-value', 99] >>> func2(args) >>> args ['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) >>> args {'a': 'new-value', 'b': 100}
または、クラスインスタンスに値をまとめます。
>>> class Namespace: ... 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 Namespace ... args.b = args.b + 1 # change object in-place ... >>> args = Namespace(a='old-value', b=99) >>> func4(args) >>> vars(args) {'a': 'new-value', 'b': 100}
これを複雑にする正当な理由はほとんどありません。
最善の選択は、複数の結果を含むタプルを返すことです。
Pythonで高階関数を作成するにはどうすればよいですか?
ネストされたスコープを使用するか、呼び出し可能なオブジェクトを使用するかの2つの選択肢があります。 たとえば、値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>
おそらく、クラスには名前があります。2つの名前にバインドされ、名前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
この構文がPython2.5で導入される前は、一般的なイディオムは論理演算子を使用することでした。
[expression] and [on_true] or [on_false]
ただし、このイディオムは、 on_true のブール値が偽の場合に誤った結果をもたらす可能性があるため、安全ではありません。 したがって、... if ... else ...
フォームを使用することをお勧めします。
難読化されたワンライナーをPythonで書くことは可能ですか?
はい。 通常、これは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
子供たち、家でこれを試さないでください!
関数のパラメータリストのスラッシュ(/)はどういう意味ですか?
関数の引数リストのスラッシュは、その前のパラメーターが位置のみであることを示します。 位置のみのパラメーターは、外部で使用可能な名前のないパラメーターです。 位置のみのパラメーターを受け入れる関数を呼び出すと、引数は位置のみに基づいてパラメーターにマップされます。 たとえば、 divmod()は、位置のみのパラメーターを受け入れる関数です。 そのドキュメントは次のようになります。
>>> help(divmod)
Help on built-in function divmod in module builtins:
divmod(x, y, /)
Return the tuple (x//y, x%y). Invariant: div*y + mod == x.
パラメータリストの最後にあるスラッシュは、両方のパラメータが位置のみであることを意味します。 したがって、キーワード引数を指定して divmod()を呼び出すと、エラーが発生します。
>>> divmod(x=3, y=4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: divmod() takes no keyword arguments
数字と文字列
16進整数と8進整数を指定するにはどうすればよいですか?
8進数を指定するには、8進数の前にゼロを付けてから、小文字または大文字の「o」を付けます。 たとえば、変数「a」を8進数の値「10」(10進数で8)に設定するには、次のように入力します。
>>> a = 0o10
>>> a
8
16進数も同様に簡単です。 16進数の前にゼロを付け、次に小文字または大文字の「x」を付けます。 16進数は、小文字または大文字で指定できます。 たとえば、Pythonインタープリターでは次のようになります。
>>> a = 0xa5
>>> a
165
>>> b = 0XB2
>>> b
178
-22 // 10が-3を返すのはなぜですか?
これは主に、i % j
がj
と同じ符号を持っているという欲求によって推進されています。 あなたがそれを望むなら、そしてまた欲しいなら:
i == (i // j) * j + (i % j)
次に、整数除算はフロアを返す必要があります。 Cはまた、そのIDを保持する必要があり、i // j
を切り捨てるコンパイラーは、i % j
がi
と同じ符号を持つようにする必要があります。
j
が負の場合、i % j
の実際の使用例はほとんどありません。 j
が正の場合、多くあり、事実上すべてにおいて、i % j
が>= 0
である方が便利です。 時計が今10と言っているとしたら、200時間前は何と言っていたのでしょうか。 -190 % 12 == 2
は便利です。 -190 % 12 == -10
は噛むのを待っているバグです。
文字列を数値に変換するにはどうすればよいですか?
整数の場合は、組み込みの int()型コンストラクターを使用します。 int('144') == 144
。 同様に、 float()は浮動小数点に変換されます。 float('144') == 144.0
。
デフォルトでは、これらは数値を10進数として解釈するため、int('0144') == 144
およびint('0x144')
は ValueError を発生させます。 int(string, base)
は、変換元のベースを2番目のオプションの引数として使用するため、int('0x144', 16) == 324
です。 基数が0として指定されている場合、数値はPythonの規則を使用して解釈されます。先頭の「0o」は8進数を示し、「0x」は16進数を示します。
文字列を数値に変換するだけの場合は、組み込み関数 eval()を使用しないでください。 eval()は大幅に遅くなり、セキュリティリスクが発生します。誰かがPython式を渡して、望ましくない副作用が発生する可能性があります。 たとえば、誰かが__import__('os').system("rm -rf $HOME")
を渡すと、ホームディレクトリが消去されます。
eval()には、数値をPython式として解釈する効果もあります。 eval('09')
は、Pythonが10進数の先頭の「0」(「0」を除く)を許可しないため、構文エラーを出します。
数値を文字列に変換するにはどうすればよいですか?
たとえば、数値144を文字列 '144'に変換するには、組み込み型コンストラクター str()を使用します。 16進数または8進数の表現が必要な場合は、組み込み関数 hex()または oct()を使用してください。 凝ったフォーマットについては、フォーマットされた文字列リテラルおよびフォーマット文字列構文セクションを参照してください。 "{:04d}".format(144)
は'0144'
を生成し、"{:.3f}".format(1.0/3.0)
は'0.333'
を生成します。
文字列をその場で変更するにはどうすればよいですか?
文字列は不変であるため、できません。 ほとんどの場合、組み立てたいさまざまなパーツから新しい文字列を作成するだけです。 ただし、インプレースUnicodeデータを変更する機能を備えたオブジェクトが必要な場合は、 io.StringIO オブジェクトまたは array モジュールを使用してみてください。
>>> 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'
文字列を使用して関数/メソッドを呼び出すにはどうすればよいですか?
いろいろなテクニックがあります。
最良の方法は、文字列を関数にマップする辞書を使用することです。 この手法の主な利点は、文字列が関数の名前と一致する必要がないことです。 これは、ケース構成をエミュレートするために使用される主要な手法でもあります。
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 '
これは通常、一度に1行ずつテキストを読む場合にのみ必要なので、S.rstrip()
をこのように使用するとうまくいきます。
パフォーマンス
私のプログラムは遅すぎます。 どうすればスピードアップできますか?
一般的に、それは難しいことです。 まず、さらにダイビングする前に覚えておくべきことのリストを次に示します。
- パフォーマンス特性は、Pythonの実装によって異なります。 このFAQは、 CPython に焦点を当てています。
- 特にI / Oやマルチスレッドについて話す場合、動作はオペレーティングシステムによって異なる可能性があります。
- コードを最適化しようとするの前にプログラム内のホットスポットを常に見つける必要があります(プロファイルモジュールを参照)。
- ベンチマークスクリプトを作成すると、改善点を検索するときにすばやく反復できます( timeit モジュールを参照)。
- 高度な最適化に隠された回帰を潜在的に導入する前に、(単体テストまたはその他の手法を通じて)適切なコードカバレッジを確保することを強くお勧めします。
そうは言っても、Pythonコードを高速化するための多くのトリックがあります。 許容可能なパフォーマンスレベルに到達するために大いに役立ついくつかの一般的な原則を次に示します。
- アルゴリズムを高速化する(または高速化する)と、コード全体にマイクロ最適化のトリックを振りかけるよりもはるかに大きなメリットが得られます。
- 適切なデータ構造を使用してください。 組み込み型およびコレクションモジュールのドキュメントを調べてください。
- 標準ライブラリが何かを行うためのプリミティブを提供する場合、(保証はされていませんが)思いついた他のどの方法よりも高速である可能性があります。 これは、ビルトインや一部の拡張タイプなど、Cで記述されたプリミティブに二重に当てはまります。 たとえば、 list.sort()組み込みメソッドまたは関連する sorted()関数のいずれかを使用して、並べ替えを実行してください(並べ替え方法を参照してください)。 中程度に高度な使用法の例)。
- 抽象化は、間接参照を作成し、インタプリタにさらに作業を強いる傾向があります。 間接化のレベルが実行された有用な作業の量を上回っている場合、プログラムは遅くなります。 特に小さな関数やメソッド(読みやすさを損なうことが多い)の形で、過度の抽象化を避ける必要があります。
純粋なPythonが許可できる限界に達した場合は、さらに先に進むためのツールがあります。 たとえば、 Cython は、わずかに変更されたバージョンのPythonコードをC拡張機能にコンパイルでき、さまざまなプラットフォームで使用できます。 Cythonは、コンパイル(およびオプションの型注釈)を利用して、コードを解釈する場合よりも大幅に高速化できます。 Cプログラミングのスキルに自信がある場合は、 C拡張モジュールを自分で作成することもできます。
多くの文字列を連結する最も効率的な方法は何ですか?
str および bytes オブジェクトは不変であるため、多くの文字列を連結すると、各連結によって新しいオブジェクトが作成されるため、非効率的です。 一般的なケースでは、合計ランタイムコストは文字列の合計長の2次式です。
多くの str オブジェクトを蓄積するには、それらをリストに配置し、最後に str.join()を呼び出すことをお勧めします。
chunks = []
for s in my_strings:
chunks.append(s)
result = ''.join(chunks)
(もう1つの合理的に効率的なイディオムは、 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番目(最後から2番目)のインデックスなどです。 seq[-n]
はseq[len(seq)-n]
と同じと考えてください。
負のインデックスを使用すると非常に便利です。 たとえば、S[:-1]
は、最後の文字を除くすべての文字列です。これは、文字列から末尾の改行を削除するのに役立ちます。
シーケンスを逆の順序で繰り返すにはどうすればよいですか?
reverse()組み込み関数を使用します。
for x in reversed(sequence):
... # do something with x ...
これは元のシーケンスには影響しませんが、逆の順序で新しいコピーを作成して繰り返します。
リストから重複を削除するにはどうすればよいですか?
これを行うための多くの方法の長い議論については、Pythonクックブックを参照してください。
リストの並べ替えを気にしない場合は、リストを並べ替えてから、リストの最後からスキャンし、重複を削除します。
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]
リストのすべての要素をセットキーとして使用できる場合(つまり、 それらはすべてハッシュ可能)これは多くの場合より高速です
mylist = list(set(mylist))
これにより、リストがセットに変換され、重複が削除されてから、リストに戻ります。
リストから複数のアイテムを削除するにはどうすればよいですか
重複を削除する場合と同様に、削除条件を使用して明示的に逆に繰り返すことは1つの可能性です。 ただし、暗黙的または明示的な順方向反復でスライス置換を使用する方が簡単で高速です。 これが3つのバリエーションです。
mylist[:] = filter(keep_function, mylist)
mylist[:] = (x for x in mylist if keep_condition)
mylist[:] = [x for x in mylist if keep_condition]
リスト内包表記が最も速い場合があります。
Pythonでどのように配列を作成しますか?
リストを使用する:
["this", 1, "is", "an", "array"]
リストは、時間計算量の点でCまたはPascal配列と同等です。 主な違いは、Pythonリストにはさまざまなタイプのオブジェクトを含めることができることです。
array
モジュールは、コンパクトな表現で固定型の配列を作成するためのメソッドも提供しますが、リストよりもインデックス付けに時間がかかります。 また、数値拡張などは、さまざまな特性を持つ配列のような構造を定義していることにも注意してください。
Lispスタイルのリンクリストを取得するには、タプルを使用してconsセルをエミュレートできます。
lisp_list = ("like", ("this", ("example", None) ) )
可変性が必要な場合は、タプルの代わりにリストを使用できます。 ここで、lispcarのアナログは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の同じリストへの3つの参照を含むリストを作成します。 1つの行への変更はすべての行に表示されますが、これはほぼ確実に希望どおりではありません。
推奨されるアプローチは、最初に目的の長さのリストを作成してから、新しく作成したリストを各要素に入力することです。
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]
追加が機能するときに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
がオブジェクトに追加されますa_tuple[0]
は(1
)を指し、結果オブジェクト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']
これが発生する理由を確認するには、(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__
が成功したため、リストが拡張されますが、result
がa_tuple[0]
が既に指しているのと同じオブジェクトを指している場合でも、その最終的な割り当てはエラーになります。タプルは不変だからです。
複雑な並べ替えをしたいのですが、Pythonでシュワルツ変換を実行できますか?
PerlコミュニティのRandalSchwartzに起因するこの手法は、リストの要素を、各要素をその「ソート値」にマップするメトリックによってソートします。 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])
これがより読みやすいと思われる場合は、最終的なリスト内包表記の代わりにこれを使用することをお勧めします。 ただし、長いリストの場合はほぼ2倍遅くなります。 どうして? まず、append()
操作ではメモリを再割り当てする必要があり、毎回それを回避するためにいくつかのトリックを使用しますが、それでも時々実行する必要があり、かなりのコストがかかります。 第2に、「result.append」という式には追加の属性ルックアップが必要です。第3に、これらすべての関数呼び出しを行う必要がなくなるため、速度が低下します。
オブジェクト
クラスとは何ですか?
クラスは、クラスステートメントを実行することによって作成される特定のオブジェクトタイプです。 クラスオブジェクトは、データ型に固有のデータ(属性)とコード(メソッド)の両方を具体化するインスタンスオブジェクトを作成するためのテンプレートとして使用されます。
クラスは、基本クラスと呼ばれる1つ以上の他のクラスに基づくことができます。 次に、基本クラスの属性とメソッドを継承します。 これにより、オブジェクトモデルを継承によって連続的に改良することができます。 メールボックスの基本的なアクセサメソッドを提供する汎用のMailbox
クラスと、さまざまな特定のメールボックスを処理するMboxMailbox
、MaildirMailbox
、OutlookMailbox
などのサブクラスがある場合があります。フォーマット。
方法とは何ですか?
メソッドは、通常x.name(arguments...)
と呼ばれるオブジェクトx
の関数です。 メソッドは、クラス定義内の関数として定義されています。
class C:
def meth(self, arg):
return arg * 2 + self.attribute
自己とは何ですか?
自己は、メソッドの最初の引数の単なる慣習的な名前です。 meth(self, a, b, c)
として定義されたメソッドは、定義が発生するクラスのインスタンスx
に対して、x.meth(a, b, c)
として呼び出される必要があります。 呼び出されたメソッドは、meth(x, a, b, c)
として呼び出されたと見なします。
メソッド定義と呼び出しで「self」を明示的に使用する必要があるのはなぜですか?も参照してください。
オブジェクトが特定のクラスのインスタンスであるか、そのサブクラスのインスタンスであるかを確認するにはどうすればよいですか?
組み込み関数isinstance(obj, cls)
を使用します。 単一のクラスの代わりにタプルを提供することで、オブジェクトがいくつかのクラスのいずれかのインスタンスであるかどうかを確認できます。 isinstance(obj, (class1, class2, ...))
、オブジェクトがPythonの組み込み型の1つであるかどうかを確認することもできます。 isinstance(obj, str)
またはisinstance(obj, (int, float, complex))
。
ほとんどのプログラムは、ユーザー定義クラスで 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()
委任とは何ですか?
委任は、オブジェクト指向の手法(デザインパターンとも呼ばれます)です。 オブジェクトx
があり、そのメソッドの1つだけの動作を変更したいとします。 変更したいメソッドの新しい実装を提供し、他のすべてのメソッドを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
(またはBase
の基本クラス)をBase.meth(self, arguments...)
として。 ここで、Base.meth
はバインドされていないメソッドであるため、self
引数を指定する必要があります。
基本クラスを簡単に変更できるようにコードを整理するにはどうすればよいですか?
基本クラスのエイリアスを定義し、クラス定義の前に実際の基本クラスを割り当てて、クラス全体でエイリアスを使用することができます。 次に、変更する必要があるのは、エイリアスに割り当てられた値だけです。 ちなみに、このトリックは動的に決定したい場合にも便利です(例: リソースの可用性に応じて)使用する基本クラス。 例:
BaseAlias = <real base class>
class Derived(BaseAlias):
def meth(self):
BaseAlias.meth(self)
...
静的クラスデータと静的クラスメソッドを作成するにはどうすればよいですか?
Pythonでは、静的データと静的メソッド(C ++またはJavaの意味で)の両方がサポートされています。
静的データの場合は、クラス属性を定義するだけです。 属性に新しい値を割り当てるには、割り当てでクラス名を明示的に使用する必要があります。
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.count
は、c
自体またはベース上のクラスによってオーバーライドされない限り、isinstance(c, C)
が成立するようなc
のC.count
も指します。 -c.__class__
からC
に戻るクラス検索パス。
注意:Cのメソッド内で、self.count = 42
のような割り当ては、self
自身のdictに「count」という名前の新しい無関係なインスタンスを作成します。 クラス静的データ名の再バインドでは、メソッド内であるかどうかに関係なく、常にクラスを指定する必要があります。
C.count = 314
静的メソッドが可能です:
class C:
@staticmethod
def static(arg1, arg2, arg3):
# No 'self' parameter!
...
ただし、静的メソッドの効果を得るはるかに簡単な方法は、単純なモジュールレベルの関数を使用することです。
def getcount():
return C.count
モジュールごとに1つのクラス(または密接に関連するクラス階層)を定義するようにコードが構造化されている場合、これにより必要なカプセル化が提供されます。
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
の形式の識別子(少なくとも2つの先頭のアンダースコア、最大で1つの末尾のアンダースコア)は、テキストで_classname__spam
に置き換えられます。ここで、classname
は、先頭にある現在のクラス名です。アンダースコアが削除されました。
これはプライバシーを保証するものではありません。外部ユーザーは引き続き意図的に「_classname__spam」属性にアクセスでき、プライベート値はオブジェクトの__dict__
に表示されます。 多くのPythonプログラマーは、プライベート変数名をわざわざ使用することはありません。
私のクラスは__del__を定義していますが、オブジェクトを削除しても呼び出されません。
これにはいくつかの理由が考えられます。
delステートメントは必ずしも__del__()
を呼び出す必要はありません。オブジェクトの参照カウントを単にデクリメントし、これがゼロに達すると__del__()
が呼び出されます。
データ構造に循環リンクが含まれている場合(例: 各子に親参照があり、各親に子のリストがあるツリー)参照カウントがゼロに戻ることはありません。 たまに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
モジュール
.pycファイルを作成するにはどうすればよいですか?
モジュールを初めてインポートするとき(または現在のコンパイル済みファイルの作成後にソースファイルが変更されたとき)、コンパイル済みコードを含む.pyc
ファイルをの__pycache__
サブディレクトリに作成する必要があります。 .py
ファイルを含むディレクトリ。 .pyc
ファイルのファイル名は、.py
ファイルと同じ名前で始まり、.pyc
で終わり、中間のコンポーネントは特定のpython
それを作成したバイナリ。 (詳細については、 PEP 3147 を参照してください。)
.pyc
ファイルが作成されない理由の1つは、ソースファイルを含むディレクトリのアクセス許可の問題です。つまり、__pycache__
サブディレクトリを作成できません。 これは、たとえば、あるユーザーとして開発したが別のユーザーとして実行した場合、たとえばWebサーバーでテストしている場合に発生する可能性があります。
PYTHONDONTWRITEBYTECODE 環境変数が設定されていない限り、モジュールをインポートする場合、.pycファイルの作成は自動的に行われ、Pythonには次の機能(権限、空き領域など)があります。 __pycache__
サブディレクトリを作成し、コンパイルされたモジュールをそのサブディレクトリに書き込みます。
トップレベルのスクリプトでPythonを実行することはインポートとは見なされず、.pyc
は作成されません。 たとえば、別のモジュールxyz.py
をインポートするトップレベルモジュールfoo.py
がある場合、foo
を実行すると(シェルコマンドとしてpython foo.py
と入力します) )、xyz
がインポートされるためxyz
に対して.pyc
が作成されますが、foo
に対して.pyc
ファイルは作成されません。 X289X] はインポートされていません。
foo
の.pyc
ファイルを作成する必要がある場合、つまり、インポートされていないモジュールの.pyc
ファイルを作成する必要がある場合は、を使用できます。 py_compile および compileall モジュール。
py_compile モジュールは、任意のモジュールを手動でコンパイルできます。 1つの方法は、そのモジュールでcompile()
関数をインタラクティブに使用することです。
>>> import py_compile
>>> py_compile.compile('foo.py')
これにより、.pyc
がfoo.py
と同じ場所にある__pycache__
サブディレクトリに書き込まれます(または、オプションのパラメータcfile
でオーバーライドできます)。
compileall モジュールを使用して、1つまたは複数のディレクトリ内のすべてのファイルを自動的にコンパイルすることもできます。 compileall.py
を実行し、コンパイルするPythonファイルを含むディレクトリのパスを指定することで、シェルプロンプトから実行できます。
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
問題は、インタプリタが次の手順を実行することです。
- 主なインポートfoo
- fooの空のグローバルが作成されます
- fooがコンパイルされ、実行を開始します
- fooはバーをインポートします
- バーの空のグローバルが作成されます
- バーがコンパイルされ、実行を開始します
- barはfooをインポートします(fooという名前のモジュールがすでに存在するため、これはノーオペレーションです)
- bar.foo_var = foo.foo_var
Pythonはまだfoo
の解釈を完了しておらず、foo
のグローバルシンボルディクショナリはまだ空であるため、最後のステップは失敗します。
import foo
を使用してから、グローバルコードでfoo.foo_var
にアクセスしようとすると同じことが起こります。
この問題には(少なくとも)3つの可能な回避策があります。
Guido van Rossumは、from <module> import ...
の使用をすべて避け、すべてのコードを関数内に配置することをお勧めします。 グローバル変数とクラス変数の初期化では、定数または組み込み関数のみを使用する必要があります。 これは、インポートされたモジュールのすべてが<module>.<name>
として参照されることを意味します。
Jim Roskindは、各モジュールで次の順序で手順を実行することを提案しています。
- エクスポート(インポートされた基本クラスを必要としないグローバル、関数、およびクラス)
import
ステートメント- アクティブなコード(インポートされた値から初期化されるグローバルを含む)。
van Rossumは、インポートが奇妙な場所に表示されるため、このアプローチをあまり好みませんが、機能します。
Matthias Urlichsは、最初から再帰的なインポートが不要になるように、コードを再構築することをお勧めします。
これらのソリューションは相互に排他的ではありません。
__import __( 'xyz')は ; どうすればzを取得できますか?
代わりに、 importlib の便利な関数 import_module()を使用することを検討してください。
z = importlib.import_module('x.y.z')
インポートしたモジュールを編集して再インポートすると、変更が表示されません。 なぜこれが起こるのですか?
効率と一貫性の理由から、Pythonはモジュールが最初にインポートされたときにのみモジュールファイルを読み取ります。 そうでない場合、各モジュールが同じ基本モジュールをインポートする多くのモジュールで構成されるプログラムでは、基本モジュールが何度も解析および再解析されます。 変更されたモジュールの再読み取りを強制するには、次のようにします。
import importlib
import modname
importlib.reload(modname)
警告:この手法は100 % f ool-proofではありません。 特に、次のようなステートメントを含むモジュール
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
クラスオブジェクトの「ID」を出力すると、問題の性質が明らかになります。
>>> hex(id(c.__class__))
'0x7352a0'
>>> hex(id(cls.C))
'0x4198d0'