内容
- エントリー
- 設計と初期化
- 任意のクラスの演算子をオーバーライドする
- クラスのプレゼンテーション
- 属性アクセス制御
- カスタムシーケンスを作成する
- 反射
- 呼び出されたオブジェクト
- コンテキストマネージャー
- 抽象基本クラス
- 記述子の構築
- コピー
- オブジェクトでpickleモジュールを使用する
- おわりに
- 付録1:マジックメソッドの呼び出し方法
- 付録2:Python 3の変更
エントリー
魔法の方法とは何ですか? それらはすべてオブジェクト指向Pythonです。 これらは、クラスに「魔法」を追加できる特別なメソッドです。 これらは常に2つのアンダースコアで囲まれています(たとえば、
__lt__
または
__lt__
)。 また、私たちが望むほど文書化されていません。 すべてのマジックメソッドはドキュメントに記載されていますが、非常にランダムで、ほとんど組織化されていません。 したがって、Pythonのドキュメントがないと私が感じているものを修正するために、理解しやすい言語で記述され、例が豊富に提供されているマジックメソッドに関する詳細情報を提供します。 このガイドをお楽しみください。 トレーニング資料、メモ、または詳細な説明として使用します。 私は魔法の方法をできるだけ明確に説明しようとしました。
設計と初期化。
誰もが最も基本的な魔法のメソッド
__init__
知っています。 これにより、オブジェクトを初期化できます。 ただし、
x = SomeClass()
と書くと、
__init__
最初に呼び出されるもので
__init__
ません。 実際、オブジェクトのインスタンスが
__new__
メソッドを作成し、引数が初期化子に渡されます。 オブジェクトのライフサイクルのもう一方の端は
__del__
メソッドです。 これら3つの魔法の方法を詳しく見てみましょう。
-
__new__(cls, [...)
これは、オブジェクトが初期化されるときに呼び出される最初のメソッドです。 クラスをパラメーターとして受け取り、その後__init__
に渡される他の引数を受け取ります。__new__
ほとんど使用されませんが、特にクラスがタプルや文字列などの不変の型から継承される場合に便利です。__new__
を__new__
するつもりはありません。なぜなら、それが頻繁に必要になるわけではないからです。しかし、この方法はとてもよくできており、 ドキュメントで詳しく説明されています 。
-
__init__(self, [...)
クラス初期化子。 元のコンストラクターが呼び出されたものはすべて渡されます(たとえば、x = SomeClass(10, 'foo')
を呼び出すと、__init__
引数として10
と'foo'
を受け取ります__init__
クラスを定義するときにほぼ普遍的に使用されます。
-
__del__(self)
__new__
と__init__
オブジェクトのコンストラクターを形成する場合、__del__
はそのデストラクターです。del x
式の動作を定義しません(したがって、このコードはx.__del__()
と同等ではありません)。 むしろ、オブジェクトがガベージコレクターに入ったときのオブジェクトの動作を決定します。 これは、ソケットやファイルオブジェクトなど、削除中に追加のクリーンアップを必要とする可能性があるオブジェクトにとって非常に便利です。 ただし、インタープリターがシャットダウンしてもオブジェクトが存続している場合は__del__
が呼び出される保証がないため、注意が必要です。 そのため、__del__
は、優れたプログラミングプラクティスの代替として機能することはできません(接続の処理が終了した場合などは、常に接続を終了します)。 実際、呼び出し保証がないため、__del__
はほとんど使用しないでください。 注意して使用してください!
翻訳者からの注意: svetlov は 、著者がここで間違えられていることに__del__
します。実際、通訳の完了時に__del__
常に呼び出されます。
それをすべてまとめると、ここに
__init__
と
__del__
が動作する例があり
__del__
:
from os.path import join class FileObject: ''' , , .''' def __init__(self, filepath='~', filename='sample.txt'): # filename filepath self.file = open(join(filepath, filename), 'r+') def __del__(self): self.file.close() del self.file
任意のクラスの演算子をオーバーライドする
Pythonでマジックメソッドを使用することの大きな利点の1つは、組み込みメソッドと同じようにオブジェクトを動作させる簡単な方法を提供することです。 これは、ベース演算子の退屈で非論理的な非標準の動作を回避できることを意味します。 一部の言語では、次のような記述が一般的です。
if instance.equals(other_instance): # do something
もちろん、Pythonでも同じことができますが、これにより混乱と不必要な冗長性が追加されます。 異なるライブラリーは同じ操作を異なる方法で呼び出し、それらを使用するプログラマーに必要以上のアクションを実行させます。 マジックメソッドの力を使用して、目的のメソッド(この場合は
__eq__
)を決定し、 念頭に置いたものを正確に表現できます 。
if instance == other_instance: #do something
これは、魔法の方法の長所の1つです。 それらの大部分は、標準演算子が何をするかを決定できるため、組み込み型であるかのようにクラスで演算子を使用できます。
魔法の比較方法
Pythonには、不器用なメソッドではなく、演算子を使用してオブジェクト間の直感的な比較を定義するために設計された多数のマジックメソッドがあります。 さらに、Pythonのデフォルトの動作をオーバーライドしてオブジェクトを比較する方法を提供します(参照により)。 これらのメソッドとその機能のリストは次のとおりです。
-
__cmp__(self, other)
最も基本的な比較方法。 実際には、すべての比較演算子(>、== 、! =、など)の動作を決定しますが、必ずしも必要な方法ではありません(たとえば、2つのインスタンスの等価性が1つの基準によって決定されるが、他のために別の)。__cmp__
は、self < other
場合は負の数、self == other
場合はゼロ、self > other
場合は正の数を返します。 ただし、通常は、すべてを__cmp__
定義するよりも、必要な各比較を定義する方が__cmp__
です。 ただし、__cmp__
は、必要なすべての比較が同じ基準で実行される場合に、繰り返しを避け、明快さを高める良い方法です。
-
__eq__(self, other)
等号演算子==
の動作を定義します。
-
__ne__(self, other)
不等式演算子の動作を定義します!!
-
__lt__(self, other)
演算子の動作を定義しますless<
-
__gt__(self, other)
演算子moreの動作>
を定義します。
-
__le__(self, other)
<=
以下の演算子の動作を定義します。
-
__ge__(self, other)
>=
以上の演算子の動作を定義します。
たとえば、単語を説明するクラスを考えます。 単語を辞書順(アルファベット順)で比較できます。これは文字列を比較するときのデフォルトの動作ですが、比較するときは、音節の長さや数など、他の基準を使用することもできます。 この例では、長さを比較します。 実装は次のとおりです。
class Word(str): ''' , .''' def __new__(cls, word): # __new__, str # ( ) if ' ' in word: print "Value contains spaces. Truncating to first space." word = word[:word.index(' ')] # Word return str.__new__(cls, word) def __gt__(self, other): return len(self) > len(other) def __lt__(self, other): return len(self) < len(other) def __ge__(self, other): return len(self) >= len(other) def __le__(self, other): return len(self) <= len(other)
これで、2つの
Word
(
Word('foo')
と
Word('bar')
)を作成し、長さを比較できます。
__eq__
と
__ne__
定義していないことに注意してください。これ
__ne__
、奇妙な動作が発生します(たとえば、
Word('foo') == Word('bar')
はtrueと見なされます)。 長さに基づいて等価性をテストする場合、これは意味をなさないため、標準の等価性テストは
str
から残し
str
。
さて、すべての比較を完全にカバーするために、それぞれの比較の魔法の方法を定義する必要はないことを言及する良い機会のようです。 標準ライブラリは
functools
モジュールにデコレータークラスを提供します。これにより、すべての比較メソッドが決定されます
__eq__
1つ(
__gt__
、
__lt__
など)のみを定義すれば十分です。この機能はPythonのバージョン2.7以降で使用可能です。あなたは満足しています、あなたは多くの時間と労力を節約します。 有効にするには、クラス定義に
@total_ordering
します。
数値魔法法
比較演算子によってオブジェクトの比較方法を決定できるように、数値演算子の動作を定義できます。 準備をしなさい、友人、それらの多くがあります。 より良い構成のために、数値マジックメソッドを5つのカテゴリに分類しました。単項演算子、通常の算術演算子、反射算術演算子(後で詳しく説明します)、複合代入、および型変換です。
単項演算子と関数
単項演算子および関数には、否定、絶対値などのオペランドが1つだけあります。
-
__pos__(self)
単項プラス(+some_object
)の動作を定義します
-
__neg__(self)
否定動作を定義します(-some_object
)
-
__abs__(self)
abs()
組み込み関数の動作を定義します。
-
__invert__(self)
~
演算子によって反転される動作を定義します。 彼がやっていることの説明については、二項演算子に関するウィキペディアの記事を参照してください。
-
__round__(self, n)
組み込みのround()
関数の動作を定義します。n
は、丸める小数点以下の桁数です。
-
__floor__(self)
math.floor()
動作を定義します。つまり、最も近い小さい整数に丸めます。
-
__ceil__(self)
math.ceil()
動作を定義します。つまり、最も近い整数に丸めます。
-
__trunc__(self)
math.trunc()
動作、つまり整数へのトリミングを定義します。
一般的な算術演算子
ここで、通常の2項演算子(およびさらに2、3の関数)、+、-、*、および同様のものを検討します。 彼らは、ほとんどの場合、自分自身を完全に説明しています。
-
__add__(self, other)
追加。
-
__sub__(self, other)
減算
-
__mul__(self, other)
乗算
-
__floordiv__(self, other)
整数除算、演算子//
-
__div__(self, other)
部門、演算子。
-
__truediv__(self, other)
正しい部門。from __future__ import division
いる場合にのみ機能することに注意してください。
-
__mod__(self, other)
除算演算子%
の残り。
-
__divmod__(self, other)
組み込みのdivmod()
関数の動作を定義します。
-
__pow__
拡張、演算子**
。
-
__lshift__(self, other)
バイナリ左シフト、演算子<<
-
__rshift__(self, other)
右へのバイナリシフト、演算子>>
-
__and__(self, other)
バイナリAND、&
演算子。
-
__or__(self, other)
バイナリOR演算子|
。
-
__xor__(self, other)
バイナリxor、演算子^
反映された算術演算子
私がより詳細に反映された算術に専念するつもりだと言ったことを覚えていますか? これはある種の大きくて恐ろしくて理解できない概念だと思うかもしれません。 実際、すべてが非常に簡単です。 以下に例を示します。
some_object + other
これは「通常の」追加です。 同等の反射式の唯一の違いは、用語の順序です:
other + some_object
したがって、これらの魔法のメソッドはすべて、通常のバージョンと同じように動作しますが、第1オペランドとして
other
し、第2オペランドとして
self
を使用して操作を実行します。 ほとんどの場合、反映された操作の結果は通常の同等の結果と同じであるため、
__radd__
を定義するときに
__radd__
呼び出しに制限することができます。 演算子の左側のオブジェクト(この例では
other
)には、このメソッドの通常の無反射バージョンを使用しないでください。 この例では、
some_object.__radd__
が
__add__
定義されていない場合にのみ
some_object.__radd__
が呼び出されます。
-
__radd__(self, other)
反映された追加。
-
__rsub__(self, other)
反映された減算。
-
__rmul__(self, other)
反映された乗算。
-
__rfloordiv__(self, other)
整数の除算、演算子//
。
-
__rdiv__(self, other)
反射部門、オペレーター。
-
__rtruediv__(self, other)
正しい除算を反映しました。from __future__ import division
いる場合にのみ機能することに注意してください。
-
__rmod__(self, other)
除算の余り、演算子%
反映。
-
__rdivmod__(self, other)
divmod(other, self)
呼び出されたときのdivmod(other, self)
divmod()
関数の動作を定義します。
-
__rpow__
草原への標高の反映、演算子**
。
-
__rlshift__(self, other)
バイナリ左シフト、演算子<<
。
-
__rrshift__(self, other)
右へのバイナリシフトの反映、演算子>>
。
-
__rand__(self, other)
反映されたバイナリAND、&
演算子。
-
__ror__(self, other)
反映されたバイナリOR演算子|
。
-
__rxor__(self, other)
反射バイナリxor、演算子^
。
複合割り当て
Pythonでは、複合代入のマジックメソッドも広く表現されています。 ほとんどの場合、すでに複合割り当てに精通している可能性が高く、これは「通常の」演算子と割り当ての組み合わせです。 それでも不明な場合は、次の例をご覧ください。
x = 5 x += 1 # x = x + 1
これらの各メソッドは、左側の変数に割り当てられる値を返す必要があります(たとえば、
a += b
場合、
__iadd__
は
a + b
に割り当てられる
a + b
を返す必要があります)。 リストは次のとおりです。
-
__iadd__(self, other)
割り当てを伴う割り当て。
-
__isub__(self, other)
代入による減算。
-
__imul__(self, other)
割り当ての乗算。
-
__ifloordiv__(self, other)
代入による整数除算、演算子//=
。
-
__idiv__(self, other)
代入による除算、演算子/=
。
-
__itruediv__(self, other)
割り当てのある分割を修正します。from __future__ import division
いる場合にのみ機能することに注意してください。
-
__imod_(self, other)
割り当て除算の残り、演算子%=
。
-
__ipow__
代入を使用してステップに拡張、演算子**=
。
-
__ilshift__(self, other)
代入を伴うバイナリ左シフト、演算子<<=
。
-
__irshift__(self, other)
割り当てを伴うバイナリ右シフト、演算子>>=
。
-
__iand__(self, other)
代入と演算子AND&=
バイナリAND。
-
__ior__(self, other)
代入を伴うバイナリOR、演算子|=
。
-
__ixor__(self, other)
代入を伴うバイナリxor、演算子^=
。
型変換マジックメソッド
さらに、Pythonには、
float()
などの組み込み型変換関数の動作を定義するための多くの魔法のメソッドがあります。 ここにあります:
-
__int__(self)
intへの型変換。
-
__long__(self)
longへの型変換。
-
__float__(self)
floatへの型変換。
-
__complex__(self)
型を複素数に変換します。
-
__oct__(self)
型を8進数に変換します。
-
__hex__(self)
型を16進数に変換します。
-
__index__(self)
オブジェクトがスライス([start:stop:step]の形式の式)で使用される場合、型をintに変換します。 リストインデックスとして使用できる数値タイプを定義する場合は、__index__
定義する必要があります。
-
__trunc__(self)
math.trunc(self)
呼び出されます。 整数型(通常は長い)に切り捨てられた値を返す必要があります。
-
__coerce__(self, other)
異なるタイプのオペランドを使用して算術を実装する方法。 タイプ変換が不可能な場合、__coerce__
はNone
を返します。 変換が可能な場合は、self
とother
から同じ型に変換されたペア(2つの要素のタプル)を返す必要があります。
クラスのプレゼンテーション
多くの場合、クラスを文字列として表現すると便利です。 Pythonには、クラスを表すときにインライン関数の動作をカスタマイズするために定義できるメソッドがいくつかあります。
-
__str__(self)
クラスのインスタンスに対して呼び出されるstr()
関数の動作を定義します。
-
__repr__(self)
クラスのインスタンスに対してrepr()
関数の動作を定義します。 ターゲットオーディエンスのstr()
との主な違い。repr()
マシン指向の出力用であり(さらに、多くの場合、有効なPythonコードである必要がありstr()
)、str()
は人々が読むためのものです。
-
__unicode__(self)
クラスのインスタンスに対して呼び出されるunicode()
関数の動作を定義します。unicode()
はstr()
に似ていますが、文字列をunicodeで返します。 注意:クライアントがクラスのインスタンスでstr()
を呼び出し、__unicode__()
のみを定義した場合、これは機能しません。 誰かがUnicodeの豪華さを持っていない場合のために、常に__str__()
を定義するようにしてください。
-
__format__(self, formatstr)
クラスのインスタンスが新しいスタイルの文字列のフォーマットに使用されるときの動作を定義します。 たとえば、"Hello, {0:abc}!".format(a)
によりa.__format__("abc")
呼び出されます。 これは、特別な書式設定オプションを提供したい独自の数値または文字列タイプを定義するのに役立ちます。
-
__hash__(self)
クラスのインスタンスに対して呼び出されるhash()
関数の動作を定義します。 このメソッドは、辞書のキーをすばやく比較するために使用される整数値を返す必要があります。 この場合、通常は__eq__
も定義する必要があることに注意してください。 次のルールを使用しますa == b
hash(a) == hash(b)
意味します。
-
__nonzero__(self)
クラスのインスタンスに対して呼び出されるbool()
関数の動作を定義します。 インスタンスをTrueまたはFalseと見なすタイミングに応じて、TrueまたはFalseを返す必要があります。
-
__dir__(self)
クラスのインスタンスで呼び出されるdir()
関数の動作を定義します。 このメソッドは、属性のリストをユーザーに返す必要があります。 通常、__dir__
定義する__dir__
ませんが、__dir__
または__getattribute__
(次のパートで説明します)を再定義した場合、または属性を動的に作成した場合、クラスをインタラクティブに使用するために不可欠です。
-
__sizeof__(self)
クラスのインスタンスでsys.getsizeof()
関数の動作を定義します。 このメソッドは、オブジェクトのサイズをバイト単位で返す必要があります。 主にC拡張で定義されたクラスに役立ちますが、それについて知ることは依然として有用です。
魔法の方法に関するマニュアルの退屈な部分(例のない部分)はほぼ完了しました。 最も基本的な魔法の方法について説明したので、今度はより高度な素材に移りましょう。
属性アクセス制御
他の言語からPythonに来た多くの人々は、クラスの実際のカプセル化の欠如について不満を述べています(例えば、パブリックアクセスメソッドでプライベート属性を定義する方法はありません)。 これは完全に真実ではありません。カプセル化に関連する多くのこと、Pythonは「マジック」を通じて実装し、メソッドとフィールドの明示的な修飾子ではありません。 参照:
-
__getattr__(self, name)
ユーザーが(まったくまたはこれまでに)存在しない属性にアクセスしようとした場合の動作を定義できます。 これは、頻繁なタイプミスのインターセプトとリダイレクト、古い属性の使用に関する警告(必要に応じてこの属性を計算して返すことができます)、または必要なときにAttributeError
巧妙に返すのに役立ちます。 , , .
-
__setattr__(self, name, value)
__getattr__
,__setattr__
. , . , . , ,__setattr__
, .
-
__delattr__
,__setattr__
, , . ,__setattr__
(del self.name
__delattr__
).
-
__getattribute__(self, name)
__getattribute__
__setattr__
__delattr__
, .__getattribute__
( ,object
). ( ,__getattr__(self, name)
). , (__getattribute__
, ). , ,__getattr__
,__getattribute__
AttributeError
。もちろん、このメソッドを使用できます(最終的にはこれが選択です)が、実際に役立つ(値を設定するのではなく、受信時に動作を再定義する必要性がはるかに低い)ケースが非常に少ないため、お勧めしません可能なエラーなしで非常に困難です。
属性へのアクセスを制御するメソッドを定義すると、問題が発生しやすくなります。 例を考えてみましょう:
def __setattr__(self, name, value): self.name = value # , , , # __setattr__(). # , self.__setattr__('name', value). # , , def __setattr__(self, name, value): self.__dict__[name] = value # #
繰り返しになりますが、Pythonの魔法の手法の力は信じられないほど素晴らしいものであり、大きな力には大きな責任が伴います。何も壊さずにマジックメソッドを正しく使用する方法を知ることが重要です。
それでは、属性アクセス制御について何を学びましたか?軽く使用する必要はありません。実際、それらは過度に強力で非論理的である傾向があります。それらが存在する理由は、特定の欲求を満たすためです。Pythonは悪いことを完全に禁止する傾向はなく、その使用を複雑にするだけです。自由は最優先事項ですので、あなたは実際に何でもやりたいことができます。アクセス制御メソッドの使用例を次に示します(
super
すべてのクラスに属性があるわけではないため、を使用していることに注意してください
__dict__
)。
class AccessCounter(object): ''', value . , value.''' def __init__(self, val): super(AccessCounter, self).__setattr__('counter', 0) super(AccessCounter, self).__setattr__('value', val) def __setattr__(self, name, value): if name == 'value': super(AccessCounter, self).__setattr__('counter', self.counter + 1) # . # , # AttributeError(name) super(AccessCounter, self).__setattr__(name, value) def __delattr__(self, name): if name == 'value': super(AccessCounter, self).__setattr__('counter', self.counter + 1) super(AccessCounter, self).__delattr__(name)]
カスタムシーケンスを作成する
Pythonには、クラスをインラインシーケンス(辞書、タプル、リスト、文字列など)のように動作させる多くの方法があります。もちろん、これらは高度な制御の不合理さと、グローバル関数のホスト全体がクラスのインスタンスと完全に機能し始める魔法のために、私のお気に入りの魔法の方法です。しかし、あらゆる種類の良いことを始める前に、プロトコルについて知る必要があります。
プロトコル
Pythonで独自のシーケンスを作成することができたので、次はプロトコルについて説明します。プロトコルは、実装する必要がある一連のメソッドを提供するという点で、他の言語のインターフェイスに少し似ています。ただし、Pythonでは、プロトコルは絶対に何の義務も負わず、必ずしもアナウンスメントの実装を必要としません。それらはおそらくガイドラインのように見えます。
プロトコルについて話しているのはなぜですか? Pythonでの任意のコンテナタイプの実装には、それらのいくつかの使用が伴うためです。最初に、不変コンテナを定義するためのプロトコル:不変コンテナを作成するには、定義
__len__
と
__getitem__
(さらにそれらについての詳細)。可変コンテナプロトコルには、不変コンテナと同じものに加えて
__setitem__
、とが必要
__delitem__
です。最後に、オブジェクトを反復可能にする場合は、
__iter__
どの反復子が返されるかを決定する必要があります。この反復子は、メソッド
__iter__
(それ自体を返す)およびを必要とする反復子プロトコルに準拠する必要があり
next
ます。
コンテナマジック
さらに遅延することなく、コンテナで使用される魔法のメソッドは次のとおりです。
-
__len__(self)
コンテナ内のアイテムの数を返します。可変および不変コンテナのプロトコルの一部。
-
__getitem__(self, key)
,self[key]
. . :TypeError
KeyError
.
-
__setitem__(self, key, value)
,self[nkey] = value
. . ,KeyError
TypeError
.
-
__delitem__(self, key)
(del self[key]
). . , .
-
__iter__(self)
. ,iter()
for x in container:
.__iter__
,self
.
-
__reversed__(self)
reversed()
. . , .
-
__contains__(self, item)
__contains__
in
およびを使用して要素の所有権を確認するように設計されていnot in
ます。なぜこれがシーケンスプロトコルの一部ではないのかと尋ねるかもしれません。__contains__
定義されていない場合、Pythonは単純に要素ごとにシーケンス全体を反復処理しTrue
、正しいものが見つかった場合に戻るためです。
-
__missing__(self, key)
__missing__
から継承するときに使用されdict
ます。存在しないキーで要素を取得しようとする場合の各ケースの動作を決定します(たとえば、辞書がd
あり、辞書のキーではないd["george"]
ときに書き込む"george"
と呼ばれますd.__missing__("george")
)。
例
例として、他の言語(Haskellなど)で遭遇する可能性のあるいくつかの機能的構成要素を実装するリストを見てみましょう。
class FunctionalList: '''- : head, tail, init, last, drop, take.''' def __init__(self, values=None): if values is None: self.values = [] else: self.values = values def __len__(self): return len(self.values) def __getitem__(self, key): # , list return self.values[key] def __setitem__(self, key, value): self.values[key] = value def __delitem__(self, key): del self.values[key] def __iter__(self): return iter(self.values) def __reversed__(self): return FunctionalList(reversed(self.values)) def append(self, value): self.values.append(value) def head(self): # return self.values[0] def tail(self): # return self.values[1:] def init(self): # return self.values[:-1] def last(self): # return self.values[-1] def drop(self, n): # n return self.values[n:] def take(self, n): # n return self.values[:n]
() . , , , ( , ?),
Counter
,
OrderedDict
,
NamedTuple
.
,
isinstance()
issubclass()
, . ここにあります:
-
__instancecheck__(self, instance)
, (isinstance(instance, class)
, .
-
__subclasscheck__(self, subclass)
, (issubclass(subclass, class)
).
これらの魔法の方法を有益に使用するためのオプションはほとんどないように思えるかもしれませんが、実際はそうでしょう。私はリフレクションの魔法の手法にあまり時間をかけたくありません。それらはあまり重要ではありませんが、Pythonのオブジェクト指向プログラミングとPython全般について重要なことを反映しています。この「何でも」では非常にまれにしか起こりません。これらの魔法のメソッドは役に立たないように見えるかもしれませんが、それらが必要になった場合、それらが存在することを覚えて喜んでいるでしょう(そして、このマニュアルを読んでいます!)。
呼び出されたオブジェクト
おそらく既にご存知のように、Pythonでは、関数はファーストクラスのオブジェクトです。これは、他のオブジェクトと同様に、それらを関数またはメソッドに渡すことができることを意味します。これは非常に強力な機能です。
特殊なマジックメソッドを使用すると、クラスのインスタンスを関数のように動作させることができます。つまり、それらを「呼び出し」、関数を引数として受け取る関数に渡すことができます。これは、Pythonプログラミングをとても楽しくするもう1つの便利な機能です。
-
__call__(self, [args...])
クラスのインスタンスを関数のように呼び出すことができます。これは主にx()
、と同じことを意味しx.__call__()
ます。__call__
任意の数の引数を取ることに注意してください。つまり、必要__call__
な数の引数を取る他の関数と同じように定義できます。
__call__
特に、インスタンスが状態を頻繁に変更するクラスで役立ちます。「呼び出し」インスタンスは、オブジェクトの状態を変更するための直感的でエレガントな方法です。例は、平面上のオブジェクトの位置を表すクラスです。
class Entity: ''', . "", .''' def __init__(self, size, x, y): self.x, self.y = x, y self.size = size def __call__(self, x, y): ''' .''' self.x, self.y = x, y # ...
コンテキストマネージャー
Python 2.5では、新しいキーワードと、コードを再利用する新しい方法であるkeywordが導入されました
with
。コンテキストマネージャの概念はPythonにとって新しいものではありませんでした(以前ライブラリの一部として実装されていました)が、PEP 343では言語構造のステータスを達成しました。あなたはすでに式を見ることができました
with
:
with open('foo.txt') as bar: # - bar
コンテキストマネージャを使用すると、オブジェクトの作成がステートメントにラップされている場合に、構成またはクリーンアップするアクションを実行できます
with
。コンテキストマネージャの動作は、2つの魔法の方法によって決定されます。
-
__enter__(self)
ステートメントによって作成されたブロックの先頭でコンテキストマネージャが行うべきことを定義しますwith
。戻り__enter__
値は、内部で作業が行われる値であることに注意してくださいwith
。
-
__exit__(self, exception_type, exception_value, traceback)
, ( ). , , with. ,exception_type
,exception_value
,traceback
None
. , ; , ,__exit__
True
. , , .
__enter__
そして、
__exit__
共通し、それらの設定やリソースの精製のための行動を説明して、特定のクラスにも有用であり得ます。これらのメソッドを使用して、さまざまなオブジェクトに共通のコンテキストマネージャーを作成できます。以下に例を示します。
class Closer: ''' close with-.''' def __init__(self, obj): self.obj = obj def __enter__(self): return self.obj # with- def __exit__(self, exception_type, exception_val, trace): try: self.obj.close() except AttributeError: # close print 'Not closable.' return True #
Closer
FTP接続(closeメソッドを持つソケット)での使用例:
>>> from magicmethods import Closer >>> from ftplib import FTP >>> with Closer(FTP('ftp.somesite.com')) as conn: ... conn.dir() ... # output omitted for brevity >>> conn.dir() # long AttributeError message, can't use a connection that's closed >>> with Closer(int(5)) as i: ... i += 1 ... Not closable. >>> i 6
ラッパーがどのように正しいオブジェクトと間違ったオブジェクトの両方を適切に処理するかをご覧ください。これは、コンテキストマネージャと魔法のメソッドの力です。標準のPythonライブラリにはcontextlibモジュールが含まれていることに注意してください。このモジュールには
contextlib.closing()
、ほぼ同じことを行うコンテキストマネージャが含まれています(オブジェクトにメソッドがない場合の処理はありません
close()
)。
抽象基本クラス
http://docs.python.org/2/library/abc.htmlを参照してください。
記述子の構築
記述子は、他のオブジェクトの属性にイベントにアクセスする(取得、変更、削除する)ロジックを追加できるクラスです。記述子は、単独で使用するためのものではありません。むしろ、それらはそれらに関連付けられたいくつかのクラスによって所有されると想定されています。記述子は、属性が互いに依存するオブジェクト指向のデータベースまたはクラスを構築するのに役立ちます。特に、記述子は、いくつかの計算システムの属性または計算された属性(開始点からグリッド上の属性によって表される点までの距離)を表すときに役立ちます。
クラスが記述子になるには
__get__
、
__set__
またはから少なくとも1つのメソッドを実装する必要があります
__delete__
。これらの魔法のメソッドを見てみましょう:
-
__get__(self, instance, instance_class)
記述子から値を返すときの動作を定義します。instance
これは、メソッドが呼び出されている記述子属性のオブジェクトです。owner
これはオブジェクトのタイプ(クラス)です。
-
__set__(self, instance, value)
記述子から値を変更するときの動作を定義します。instance
これは、メソッドが呼び出されている記述子属性のオブジェクトです。value
この値はハンドルに設定されます。
-
__delete__(self, instance)
記述子から値を削除する動作を定義します。instance
これは、記述子を所有するオブジェクトです。
記述子の便利な使用例:単位変換。
class Meter(object): ''' .''' def __init__(self, value=0.0): self.value = float(value) def __get__(self, instance, owner): return self.value def __set__(self, instance, value): self.value = float(value) class Foot(object): ''' .''' def __get__(self, instance, owner): return instance.meter * 3.2808 def __set__(self, instance, value): instance.meter = float(value) / 3.2808 class Distance(object): ''', , .''' meter = Meter() foot = Foot()
コピー
Pythonでは、代入演算子はオブジェクトをコピーせず、別のリンクを追加するだけです。ただし、可変要素を含むコレクションの場合、あるシーケンスの要素を別のシーケンスに影響を与えずに変更できるように、完全なコピーが必要になる場合があります。ここに登場し
copy
ます。幸いなことに、Pythonのモジュールには心がありません。そのため、突然制御不能に自分自身をコピーし始め、すぐにLinuxロボットが惑星全体を埋め尽くすことを心配する必要はありません。
-
__copy__(self)
copy.copy()
.copy.copy()
— , , . , .
-
__deepcopy__(self, memodict={})
copy.deepcopy()
.copy.deepcopy()
— .memodict
, , . - ,copy.deepcopy()
memodict
.
これらの魔法の方法をいつ使用するか?いつものように-いずれの場合でも、標準的な動作以上のものが必要な場合。たとえば、キャッシュをディクショナリ(おそらく非常に大きなディクショナリ)として含むオブジェクトをコピーしようとする場合、キャッシュ全体をコピーする必要はなく、オブジェクトの共有メモリに1つだけコピーする必要があります。
オブジェクトでpickleモジュールを使用する
PickleはPythonデータ構造をシリアル化するためのモジュールであり、オブジェクトの状態を保存して後で復元する必要がある場合(通常はキャッシュ目的)に非常に役立ちます。さらに、経験と混乱の優れた源でもあります。
シリアル化は非常に重要なので、モジュール(
pickle
)に加えて、独自のプロトコルと独自のマジックメソッドを持っています。しかし、最初に、pickleを使用して既存のデータ型をシリアル化する方法(既に知っている場合はスキップしてください)。
シリアライズについて簡単に
シリアル化に飛び込みましょう。後で保存して復元したい辞書があるとします。その内容をファイルに書き込み、正しい構文で記述していることを確認してから、ファイルを実行
exec()
または読み取ることにより、復元する必要があります。しかし、これはせいぜい危険です:重要なデータをテキストで保存すると、プログラムをクラッシュさせたり、一般的にはコンピューターで危険なコードを実行したりするために、さまざまな方法でデータを破損または修正できます。pickleを使用する方が良い:
import pickle data = {'foo': [1, 2, 3], 'bar': ('Hello', 'world!'), 'baz': True} jar = open('data.pkl', 'wb') pickle.dump(data, jar) # jar jar.close()
そして今、数時間後、再び辞書が必要になります。
import pickle pkl_file = open('data.pkl', 'rb') # data = pickle.load(pkl_file) # print data pkl_file.close()
どうしたまさに期待されたもの。
data
いつもそこにあるかのように。
さて、注意について少し:ピクルスは完璧ではありません。そのファイルは、偶然または意図的に簡単に破損します。Pickleはテキストファイルよりも安全かもしれませんが、悪意のあるコードを実行するために使用できます。さらに、Pythonの異なるバージョン間では互換性がないため、pickleを使用してオブジェクトを配布する場合、すべての人がそれらを使用できるとは考えないでください。ただし、モジュールは、キャッシュやその他の一般的なシリアル化タスクのための強力なツールになる可能性があります。
独自のオブジェクトのシリアル化。
pickleモジュールはビルトイン型専用ではありません。プロトコルを実装するすべてのクラスで使用できます。このプロトコルには、pickleがそれらを処理する方法を設定できるオプションのメソッドが4つ含まれています(C拡張機能にはいくつかの違いがありますが、これはガイドの範囲外です)。
-
__getinitargs__(self)
直列化解除後にクラスを呼び出したい場合は__init__
、__getinitargs__
どれが引数のタプルを返すかを決定できます__init__
。このメソッドは古いスタイルのクラスでのみ機能することに注意してください。
-
__getnewargs__(self)
新しいスタイルのクラスの場合、__new__
逆シリアル化中に渡すパラメーターを決定できます。このメソッドは、に送信される引数のタプルも返す必要があり__new__
ます。
-
__getstate__(self)
__dict__
, , .__setstate__
.
-
__setstate__(self, state)
__setstate__
, ,__dict__
.__getstate__
: , , .
-
__reduce__(self)
( Python's C API), , , .__reduce__()
, . , , , . 2 5 : , , , , ,__setstate__
(), () ().
-
__reduce_ex__(self, protocol)
実装するときプロトコルバージョンを知るのは時々役に立ちます__reduce__
。そして、これは代わりに実装することで達成できます__reduce_ex__
。__reduce_ex__
実装されている場合、呼び出し設定が与えられます(__reduce__
下位互換性のために実装する必要があります)。
例
たとえば、スレートボード(
Slate
)について説明します。このボードには、何がいつ書かれたかが記憶されています。ただし、具体的には、このボードはシリアル化されるたびにクリーンになります。現在の値は保存されません。
import time class Slate: ''', . .''' def __init__(self, value): self.value = value self.last_change = time.asctime() self.history = {} def change(self, new_value): # . . self.history[self.last_change] = self.value self.value = new_value self.last_change = time.asctime() def print_changes(self): print 'Changelog for Slate object:' for k, v in self.history.items(): print '%s\t %s' % (k, v) def __getstate__(self): # self.value or self.last_change. # " " . return self.history def __setstate__(self, state): self.history = state self.value, self.last_change = None, None
おわりに
このガイドの目的は、Pythonやオブジェクト指向プログラミングの経験に関係なく、読んだすべての人に何かを伝えることです。Pythonを初めて使用する場合は、機能的でエレガントで使いやすいクラスを作成するための基本についての貴重な知識を身につけています。あなたが中級レベルのプログラマーであるなら、あなたとあなたのクライアントによって書かれたコードの量を減らすいくつかの良い新しいアイデアと戦略、いくつかの良い方法を見つけたかもしれません。あなたが専門のピトニストであるならば、あなたはあなたのおそらく忘れられた知識のいくつかを更新したか、あるいはいくつかの新しいトリックを見つけたかもしれません。あなたのレベルに関係なく、私は特別なPythonメソッドのこの旅が本当に魔法であったことを望みます(抵抗できませんでした)。
付録1:マジックメソッドの呼び出し方法
マジックメソッドの一部は、組み込み関数に直接関連しています。この場合、それらを呼び出す方法は非常に明白です。ただし、これは常にそうではありません。このアドオンは、マジックメソッドの呼び出しにつながる明白でない構文を明らかにすることを目的としています。
マジックメソッド | 呼び出されたとき(例) | 説明 |
---|---|---|
__new__(cls [,...])
| instance = MyClass(arg1, arg2)
| __new__
インスタンス化時に呼び出される |
__init__(self [,...])
| instance = MyClass(arg1, arg2)
| __init__
インスタンス化時に呼び出される |
__cmp__(self, other)
| self == other
、 self > other
、等 | 比較のために呼び出されます。 |
__pos__(self)
| +self
| 単項プラス記号 |
__neg__(self)
| -self
| 単項マイナス記号 |
__invert__(self)
| ~self
| ビット単位の反転 |
__index__(self)
| x[self]
| オブジェクトがインデックスとして使用されるときに変換する |
__nonzero__(self)
| bool(self)
、 if self:
| オブジェクトのブール値 |
__getattr__(self, name)
| self.name # name
| 存在しない属性を取得しようとしています |
__setattr__(self, name, val)
| self.name = val
| 任意の属性への割り当て |
__delattr__(self, name)
| del self.name
| 属性を削除 |
__getattribute__(self, name)
| self.name
| 属性を取得する |
__getitem__(self, key)
| self[key]
| インデックスを介してアイテムを取得する |
__setitem__(self, key, val)
| self[key] = val
| インデックスを介してアイテムに割り当てる |
__delitem__(self, key)
| del self[key]
| インデックスからアイテムを削除する |
__iter__(self)
| for x in self
| 反復 |
__contains__(self, value)
| value in self
、 value not in self
| で所有権を確認する in
|
__call__(self [,...])
| self(args)
| 「呼び出し」インスタンス |
__enter__(self)
| with self as x:
| with
コンテキストマネージャー演算子 |
__exit__(self, exc, val, trace)
| with self as x:
| with
コンテキストマネージャー演算子 |
__getstate__(self)
| pickle.dump(pkl_file, self)
| 連載 |
__setstate__(self)
| data = pickle.load(pkl_file)
| 連載 |
この表が、魔法のメソッドを呼び出すための構文とは何かについての疑問を解決することを願っています。
補足2:Python 3の変更
Python 3がオブジェクトモデルの点で2.xと異なるいくつかの主なケースについて説明します。
- 文字列とUnicodeとの間のPython 3の差がなくなっているので、
__unicode__
消失、及び登場__bytes__
(どのような挙動__str__
及び__unicode__
2.7)を構築するための新しい組み込み配列バイト機能します。 - Python 3の除算がデフォルトの「正しい除算」である
__div__
ため、これ以上はありません。 -
__coerce__
冗長性と奇妙な動作のために、これ以上はありません。 -
__cmp__
冗長性のためこれ以上。 -
__nonzero__
に改名されました__bool__
。 -
next
at iteratorsに名前が変更されました__next__
。