プログラミングに関するFAQ
コンテンツ
- プログラミングに関するFAQ
- 一般的な質問
- コア言語
- 変数に値があるのにUnboundLocalErrorが発生するのはなぜですか?
- Pythonのローカル変数とグローバル変数のルールは何ですか?
- 異なる値を持つループで定義されたラムダがすべて同じ結果を返すのはなぜですか?
- モジュール間でグローバル変数を共有するにはどうすればよいですか?
- モジュールでインポートを使用するための「ベストプラクティス」は何ですか?
- デフォルト値がオブジェクト間で共有されるのはなぜですか?
- オプションまたはキーワードパラメータをある関数から別の関数に渡すにはどうすればよいですか?
- 引数とパラメータの違いは何ですか?
- リスト「y」を変更するとリスト「x」も変更されたのはなぜですか?
- 出力パラメーター(参照による呼び出し)を使用して関数を作成するにはどうすればよいですか?
- Pythonで高階関数を作成するにはどうすればよいですか?
- Pythonでオブジェクトをコピーするにはどうすればよいですか?
- オブジェクトのメソッドまたは属性を見つけるにはどうすればよいですか?
- コードはどのようにしてオブジェクトの名前を検出できますか?
- コンマ演算子の優先順位はどうなっていますか?
- Cの「?:」三項演算子に相当するものはありますか?
- 難読化されたワンライナーをPythonで書くことは可能ですか?
- 数字と文字列
- 16進整数と8進整数を指定するにはどうすればよいですか?
- -22 // 10が-3を返すのはなぜですか?
- 文字列を数値に変換するにはどうすればよいですか?
- 数値を文字列に変換するにはどうすればよいですか?
- 文字列をその場で変更するにはどうすればよいですか?
- 文字列を使用して関数/メソッドを呼び出すにはどうすればよいですか?
- 文字列から末尾の改行を削除するためのPerlのchomp()に相当するものはありますか?
- scanf()またはsscanf()に相当するものはありますか?
- 'UnicodeError:ASCII [decoding、encoding] error:ordinal not in range(128)'はどういう意味ですか?
- シーケンス(タプル/リスト)
- 辞書
- オブジェクト
- クラスとは何ですか?
- 方法とは何ですか?
- 自己とは何ですか?
- オブジェクトが特定のクラスのインスタンスであるか、そのサブクラスのインスタンスであるかを確認するにはどうすればよいですか?
- 委任とは何ですか?
- 基本クラスで定義されたメソッドを、それをオーバーライドする派生クラスから呼び出すにはどうすればよいですか?
- 基本クラスを簡単に変更できるようにコードを整理するにはどうすればよいですか?
- 静的クラスデータと静的クラスメソッドを作成するにはどうすればよいですか?
- Pythonでコンストラクター(またはメソッド)をオーバーロードするにはどうすればよいですか?
- __spamを使おうとすると、_SomeClassName__spamに関するエラーが発生します。
- 私のクラスは__del__を定義していますが、オブジェクトを削除しても呼び出されません。
- 特定のクラスのすべてのインスタンスのリストを取得するにはどうすればよいですか?
id()
の結果が一意ではないように見えるのはなぜですか?
- モジュール
一般的な質問
ブレークポイント、シングルステップなどを備えたソースコードレベルのデバッガーはありますか?
はい。
pdbモジュールは、Python用のシンプルですが適切なコンソールモードデバッガーです。 これは標準のPythonライブラリの一部であり、ライブラリリファレンスマニュアルに記載されています。 例としてpdbのコードを使用して、独自のデバッガーを作成することもできます。
標準のPythonディストリビューション(通常はツール/スクリプト/アイドルとして利用可能)の一部であるIDLEインタラクティブ開発環境には、グラフィカルデバッガーが含まれています。
PythonWinは、pdbに基づくGUIデバッガーを含むPythonIDEです。 Pythonwinデバッガーはブレークポイントに色を付け、Pythonwin以外のプログラムのデバッグなどの優れた機能を多数備えています。 Pythonwinは、 Python for Windows Extensions プロジェクトの一部として、およびActivePythonディストリビューションの一部として利用できます( https://www.activestate.com/activepython を参照)。
Boa Constructor は、wxWidgetsを使用するIDEおよびGUIビルダーです。 視覚的なフレームの作成と操作、オブジェクトインスペクター、オブジェクトブラウザー、継承階層、ドキュメント文字列で生成されたhtmlドキュメント、高度なデバッガー、統合ヘルプ、Zopeサポートなどのソースに関する多くのビューを提供します。
Eric は、PyQtとScintilla編集コンポーネント上に構築されたIDEです。
Pydbは、標準のPythonデバッガーpdbのバージョンであり、人気のあるグラフィカルデバッガーフロントエンドであるDDD(Data Display Debugger)で使用するために変更されています。 Pydbは http://bashdb.sourceforge.net/pydb/ にあり、DDDは https://www.gnu.org/software/dddにあります。
グラフィカルデバッガーを含む多くの商用PythonIDEがあります。 それらが含まれます:
- 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バイナリとバインドして、単一の実行可能ファイルを生成するツールは多数あります。
1つは、PythonソースツリーにTools/freeze
として含まれているフリーズツールを使用することです。 PythonバイトコードをC配列に変換します。 Cコンパイラを使用すると、すべてのモジュールを新しいプログラムに埋め込んで、標準のPythonモジュールにリンクすることができます。
これは、ソースを再帰的にスキャンしてインポートステートメント(両方の形式)を探し、標準のPythonパスとソースディレクトリ(組み込みモジュールの場合)でモジュールを探すことで機能します。 次に、Pythonで記述されたモジュールのバイトコードをCコード(マーシャルモジュールを使用してコードオブジェクトに変換できる配列初期化子)に変換し、実際に使用される組み込みモジュールのみを含むカスタムメイドの構成ファイルを作成します。プログラム。 次に、生成されたCコードをコンパイルし、それをPythonインタープリターの残りの部分とリンクして、スクリプトとまったく同じように機能する自己完結型のバイナリを形成します。
明らかに、フリーズにはCコンパイラが必要です。 そうでない他のいくつかのユーティリティがあります。 1つはThomasHellerのpy2exe(Windowsのみ)です。
もう1つのツールは、AnthonyTuiningaの cx_Freeze です。
私のプログラムは遅すぎます。 どうすればスピードアップできますか?
一般的に、それは難しいことです。 Pythonコードを高速化するためのトリックはたくさんあります。 最後の手段として、Cでパーツを書き換えることを検討してください。
場合によっては、PythonをCまたはx86アセンブリ言語に自動的に翻訳することが可能です。つまり、速度を上げるためにコードを変更する必要はありません。
Pyrex は、わずかに変更されたバージョンのPythonコードをC拡張機能にコンパイルでき、さまざまなプラットフォームで使用できます。
Psyco は、Pythonコードをx86アセンブリ言語に変換するジャストインタイムコンパイラです。 あなたがそれを使うことができれば、サイコは重要な機能のために劇的なスピードアップを提供することができます。
この回答の残りの部分では、Pythonコードからもう少し速度を引き出すためのさまざまなトリックについて説明します。 プロファイリングによって特定の関数がコード内で頻繁に実行されるホットスポットであることが示された後は、必要であることがわかっている場合を除いて、最適化トリックを適用しないでください。 最適化により、ほとんどの場合、コードの明確さが低下します。結果として得られるパフォーマンス上の利点に見合う価値がない限り、明確さの低下(開発時間の増加、バグの可能性の増加)のコストを支払うべきではありません。
ウィキにはパフォーマンスのヒントに関するページがあります。
Guido van Rossumは、 https://www.python.org/doc/essays/list2strで最適化に関連する逸話を書いています。
注意すべきことの1つは、関数と(特に)メソッドの呼び出しはかなり高価であるということです。 インスタンス変数を取得または設定したり、別のメソッドを呼び出したりするだけの小さな関数を多数備えた純粋なオブジェクト指向インターフェースを設計した場合は、インスタンス変数に直接アクセスするなど、より直接的な方法を使用することを検討してください。 また、標準モジュールプロファイルも参照してください。これにより、プログラムがほとんどの時間を費やしている場所を見つけることができます(ある程度の忍耐力がある場合、プロファイリング自体によってプログラムの速度が1桁遅くなる可能性があります)。
他のプログラミング経験から知っている可能性のある多くの標準的な最適化ヒューリスティックがPythonに適用される可能性があることを忘れないでください。 たとえば、カーネルシステムコールのオーバーヘッドを削減するために、小さい書き込みよりも大きい書き込みを使用して出力デバイスに出力を送信する方が高速な場合があります。 したがって、すべての出力を「ワンショット」で書き込むCGIスクリプトは、多数の小さな出力を書き込むスクリプトよりも高速である可能性があります。
また、必要に応じてPythonのコア機能を使用してください。 たとえば、スライスを使用すると、プログラムは、高度に最適化されたC実装を使用して、インタプリタのメインループの1ティックでリストやその他のシーケンスオブジェクトを切り刻むことができます。 したがって、次と同じ効果を得るには:
L2 = []
for i in range(3):
L2.append(L1[i])
使用するのははるかに短く、はるかに高速です
L2 = list(L1[:3]) # "list" is redundant if L1 is a list.
map()、 zip()などの機能指向の組み込み関数は、単一のタスクを実行するループの便利なアクセラレーターになる可能性があることに注意してください。 たとえば、2つのリストの要素をペアにする場合:
>>> 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
よりもはるかに高速である可能性があります。これは、「合計」が多くの部分式を計算するのに対し、 [ X181X]は1回のパスですべてのコピーを実行します。 文字列を操作するには、文字列オブジェクトでreplace()
およびformat()
メソッドを使用します。 正規表現は、一定の文字列パターンを処理していない場合にのみ使用してください。 古い% o操作 string % tuple
およびstring % dictionary
を引き続き使用できます。
必ずlist.sort()
の組み込みメソッドを使用してソートを行ってください。適度に高度な使用例については、ソートミニHOWTO を参照してください。 list.sort()
は、最も極端な状況を除くすべての状況で、他のソート手法よりも優れています。
もう1つの一般的なトリックは、「ループを関数またはメソッドにプッシュする」ことです。 たとえば、実行速度が遅いプログラムがあり、プロファイラーを使用して、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
2つの例をlist = ffseq(oldlist)
と次のように書き直します。
for value in ffseq(sequence):
... # do something with value...
ff(x)
への1回の呼び出しは、ほとんどペナルティなしで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
デフォルトの引数を使用して、実行時ではなくコンパイル時に値を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
がループ内のその時点で持っていた値と同じになります。 。 つまり、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など。
- ローカルで開発されたモジュール
明示的な相対パッケージインポートのみを使用してください。 package.sub.m1
モジュールにあるコードを記述していて、package.sub.m2
をインポートしたい場合は、合法であっても、import m2
だけを記述しないでください。 代わりにfrom package.sub import m2
またはfrom . import m2
と書いてください。
循環インポートの問題を回避するために、インポートを関数またはクラスに移動する必要がある場合があります。 ゴードンマクミランは言う:
両方のモジュールが「インポート」を使用する場合、循環インポートは問題ありません。 」形式のインポート。 2番目のモジュールが最初のモジュールから名前を取得する必要があり(「モジュールのインポート名から」)、インポートが最上位にある場合、これらは失敗します。 これは、最初のモジュールが2番目のモジュールのインポートでビジー状態であるため、1番目の名前がまだ使用できないためです。
この場合、2番目のモジュールが1つの関数でのみ使用されると、インポートをその関数に簡単に移動できます。 インポートが呼び出されるまでに、最初のモジュールは初期化を完了し、2番目のモジュールはそのインポートを実行できます。
一部のモジュールがプラットフォーム固有である場合は、インポートをコードのトップレベルから移動する必要がある場合もあります。 その場合、ファイルの先頭にあるすべてのモジュールをインポートすることさえできない場合があります。 この場合、対応するプラットフォーム固有のコードに正しいモジュールをインポートすることをお勧めします。
循環インポートの回避などの問題を解決する必要がある場合、またはモジュールの初期化時間を短縮しようとしている場合にのみ、インポートを関数定義内などのローカルスコープに移動します。 この手法は、プログラムの実行方法によってはインポートの多くが不要な場合に特に役立ちます。 モジュールがその関数でのみ使用される場合は、インポートを関数に移動することもできます。 モジュールの初期化は1回であるため、モジュールを最初にロードするのはコストがかかる場合がありますが、モジュールを複数回ロードするのは事実上無料であり、辞書を数回検索するだけで済みます。 モジュール名がスコープ外になった場合でも、モジュールはおそらく sys.modules で入手できます。
オプションまたはキーワードパラメータをある関数から別の関数に渡すにはどうすればよいですか?
関数のパラメーターリストの*
および**
指定子を使用して引数を収集します。 これにより、位置引数がタプルとして、キーワード引数が辞書として提供されます。 次に、*
および**
を使用して、別の関数を呼び出すときにこれらの引数を渡すことができます。
def f(x, *args, **kwargs):
...
kwargs['width'] = '14.3c'
...
g(x, *args, **kwargs)
万が一、2.0より古いPythonバージョンが気になる場合は、 apply()を使用してください。
def f(x, *args, **kwargs):
...
kwargs['width'] = '14.3c'
...
apply(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 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で高階関数を作成するにはどうすればよいですか?
ネストされたスコープを使用するか、呼び出し可能なオブジェクトを使用するかの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 instance at 0x16D07CC>
>>> print a
<__main__.A instance 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の「?:」三項演算子に相当するものはありますか?
はい、この機能はPython2.5で追加されました。 構文は次のようになります。
[on_true] if [expression] else [on_false]
x, y = 50, 25
small = x if x < y else y
2.5より前のバージョンの場合、答えは「いいえ」になります。
難読化されたワンライナーをPythonで書くことは可能ですか?
はい。 通常、これはラムダ内にラムダをネストすることによって行われます。 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
子供たち、家でこれを試さないでください!
数字と文字列
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
は噛むのを待っているバグです。
ノート
Python 2では、__future__.division
が有効になっていない場合、a / b
はa // b
と同じ値を返します。 これは「クラシック」部門としても知られています。
文字列を数値に変換するにはどうすればよいですか?
整数の場合は、組み込みの 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の規則を使用して解釈されます。先頭の「0」は8進数を示し、「0x」は16進数を示します。
文字列を数値に変換するだけの場合は、組み込み関数 eval()を使用しないでください。 eval()は大幅に遅くなり、セキュリティリスクが発生します。誰かがPython式を渡して、望ましくない副作用が発生する可能性があります。 たとえば、誰かが__import__('os').system("rm -rf $HOME")
を渡すと、ホームディレクトリが消去されます。
eval()には、数値をPython式として解釈する効果もあります。 eval('09')
は、Pythonが「0」で始まる数値を8進数(基数8)と見なすため、構文エラーを出します。
数値を文字列に変換するにはどうすればよいですか?
たとえば、数値144を文字列 '144'に変換するには、組み込み型コンストラクター str()を使用します。 16進数または8進数の表現が必要な場合は、組み込み関数 hex()または oct()を使用してください。 凝ったフォーマットについては、フォーマット文字列構文セクションを参照してください。 "{:04d}".format(144)
は'0144'
を生成し、"{:.3f}".format(1.0/3.0)
は'0.333'
を生成します。 Python 2では、除算(/)演算子は、引数がintまたはlongの場合、除算の数学結果の下限を返しますが、引数がfloatまたは複素数の場合、除算結果の妥当な近似値を返します。
>>> print('{:.3f}'.format(1/3))
0.000
>>> print('{:.3f}'.format(1.0/3))
0.333
Python 3では、除算演算子のデフォルトの動作( PEP 238 を参照)が変更されましたが、division
をインポートすると、Python2でも同じ動作をすることができます。 __ future __ から:
>>> from __future__ import division
>>> print('{:.3f}'.format(1/3))
0.333
文字列には % o perator を使用することもできます。 詳細については、ライブラリリファレンスマニュアルを参照してください。
文字列をその場で変更するにはどうすればよいですか?
文字列は不変であるため、できません。 この機能を備えたオブジェクトが必要な場合は、文字列をリストに変換するか、配列モジュールを使用してみてください。
>>> 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'
文字列を使用して関数/メソッドを呼び出すにはどうすればよいですか?
いろいろなテクニックがあります。
最良の方法は、文字列を関数にマップする辞書を使用することです。 この手法の主な利点は、文字列が関数の名前と一致する必要がないことです。 これは、ケース構成をエミュレートするために使用される主要な手法でもあります。
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 '
これは通常、一度に1行ずつテキストを読む場合にのみ必要なので、S.rstrip()
をこのように使用するとうまくいきます。
古いバージョンのPythonの場合、2つの部分的な代替があります。
- 末尾の空白をすべて削除する場合は、文字列オブジェクトの
rstrip()
メソッドを使用します。 これにより、1つの改行だけでなく、末尾の空白がすべて削除されます。 - それ以外の場合、文字列
S
に1行しかない場合は、S.splitlines()[0]
を使用します。
scanf()またはsscanf()に相当するものはありますか?
そうではありません。
単純な入力解析の場合、最も簡単なアプローチは通常、文字列オブジェクトの split()メソッドを使用して行を空白で区切られた単語に分割し、 int()[を使用して10進文字列を数値に変換することです。 X226X]または float()。 split()
は、オプションの「sep」パラメーターをサポートしています。これは、行が空白以外のものを区切り文字として使用している場合に役立ちます。
より複雑な入力解析の場合、正規表現はCのsscanf()
よりも強力であり、タスクにより適しています。
'UnicodeError:ASCII [decoding、encoding] error:ordinal not in range(128)'はどういう意味ですか?
このエラーは、Pythonインストールで7ビットのASCII文字列しか処理できないことを示しています。 問題を修正または回避するには、いくつかの方法があります。
プログラムが任意の文字セットエンコーディングでデータを処理する必要がある場合、アプリケーションが実行される環境は通常、渡されるデータのエンコーディングを識別します。 そのエンコーディングを使用して、入力をUnicodeデータに変換する必要があります。 たとえば、電子メールまたは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番目(最後から2番目)のインデックスなどです。 seq[-n]
はseq[len(seq)-n]
と同じと考えてください。
負のインデックスを使用すると非常に便利です。 たとえば、S[:-1]
は、最後の文字を除くすべての文字列です。これは、文字列から末尾の改行を削除するのに役立ちます。
シーケンスを逆の順序で繰り返すにはどうすればよいですか?
reverse()組み込み関数を使用します。これはPython2.4の新機能です。
for x in reversed(sequence):
... # do something with x ...
これは元のシーケンスには影響しませんが、逆の順序で新しいコピーを作成して繰り返します。
Python 2.3では、拡張スライス構文を使用できます。
for x in sequence[::-1]:
... # 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]
リストのすべての要素を辞書キーとして使用できる場合(つまり、 それらはすべてハッシュ可能です)これは多くの場合より高速です
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
モジュールは、コンパクトな表現で固定型の配列を作成するためのメソッドも提供しますが、リストよりもインデックス付けに時間がかかります。 また、数値拡張などは、さまざまな特性を持つ配列のような構造を定義していることにも注意してください。
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]
より一般的には、次の関数を試すことができます。
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
がオブジェクトに追加されます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]
が既に指しているのと同じオブジェクトを指している場合でも、その最終的な割り当てはエラーになります。タプルは不変だからです。
辞書
一貫した順序でキーを表示する辞書を取得するにはどうすればよいですか?
できません。 辞書はキーを予測できない順序で格納するため、辞書の要素の表示順序も同様に予測できません。
印刷可能なバージョンをファイルに保存し、いくつかの変更を加えてから、他の印刷された辞書と比較したい場合、これはイライラする可能性があります。 この場合、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コミュニティのRandalSchwartzに起因するこの手法は、リストの要素を、各要素をその「ソート値」にマップするメトリックによってソートします。 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])
これがより読みやすいと思われる場合は、最終的なリスト内包表記の代わりにこれを使用することをお勧めします。 ただし、長いリストの場合はほぼ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, long, 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()
クラシッククラスを使用している場合:class Derived(Base): ...
などのクラス定義の場合、Base
(またはBase
のいずれか)で定義されているメソッドmeth()
を呼び出すことができます。基本クラス)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
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
モジュールごとに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 モジュールを使用することです。これにより、参照カウントを増やすことなくオブジェクトを指すことができます。 たとえば、ツリーデータ構造では、親参照と兄弟参照に弱参照を使用する必要があります(必要な場合)。
オブジェクトが、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
ファイルが作成されない理由の1つは、ディレクトリのアクセス許可の問題です。 これは、たとえば、あるユーザーとして開発したが別のユーザーとして実行した場合、たとえばWebサーバーでテストしている場合に発生する可能性があります。 モジュールをインポートしていて、Pythonにコンパイル済みモジュールをディレクトリに書き戻す機能(権限、空き領域など)がある場合、.pycファイルの作成は自動的に行われます。
トップレベルのスクリプトでPythonを実行することはインポートとは見なされず、.pyc
は作成されません。 たとえば、別のモジュールxyz.py
をインポートするトップレベルモジュールfoo.py
がある場合、foo
を実行すると、xyz
がインポートされますが、foo.py
がインポートされていないため、foo.pyc
ファイルは作成されません。
foo.pyc
を作成する必要がある場合、つまり、インポートされていないモジュールの.pyc
ファイルを作成する必要がある場合は、 py_compile および compileallを使用できます。 モジュール。
py_compile モジュールは、任意のモジュールを手動でコンパイルできます。 1つの方法は、そのモジュールでcompile()
関数をインタラクティブに使用することです。
>>> import py_compile
>>> py_compile.compile('foo.py')
これにより、.pyc
がfoo.py
と同じ場所に書き込まれます(または、オプションのパラメーター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 modname
reload(modname)
警告:この手法は100 % f ool-proofではありません。 特に、次のようなステートメントを含むモジュール
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>