クラスの魔法のメソッドを見る
classA:
pass
dir(A) #クラスのすべてのパブリックメンバーを取得できます
出力は次のとおりです
['__ class__','__delattr__','__dict__','__dir__','__doc__','__eq__','__format__','__ge__','__getattribute__','__gt__','__hash__','__init__','__le__','__lt__','__module__','__ne__','__new__','__reduce__','__reduce_ex__','__repr__','__setattr__','__sizeof__','__str__','__subclasshook__','__weakref__']
Pythonでは、** __
の二重下線で囲まれたすべてのメソッドは、まとめてマジックメソッドと呼ばれます**。たとえば、最も一般的な __init__
。
__ new__
: object .__ new __(cls)
クラスの作成方法:コンストラクター__ del__
:クラスの削除:デストラクタ__ init__
:初期化関数classA:
def __new__(cls,*args,**kwargs):print('new')return object.__new__(cls)
def __init__(self):print('init')
self.x =3
def __del__(self):print('del')A() #クラスを返す<__main__.A at 0x7f4a84767978>
# 出力
newinit
a =A()
del a #出力デル
インスタンススペースが再利用されるたびに(ガベージコレクション中に)、 __del__
が自動的に実行されます。
classPoint:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):returnPoint(self.x + other.x, self.y + other.y)
def __sub__(self, other):returnPoint(self.x - other.x, self.y - other.y)
a =Point(0,0)
b =Point(3,5)
c = a + b
c +=Point(4,6)print(c.x, c.y) # 7,11
p =Point(3,5)-Point(2,1)print(p.x, p.y) # 1,4
クラスが加算および減算操作に対応するマジックメソッドを実装している限り、加算および減算操作はクラスのオブジェクト間で実行できます。加算の具体的な実現は __add__
であり、減算の具体的な実現は __sub__
です。
オペレーターのオーバーロードを使いすぎないでください
Point.__add__ = lambda self, value: self - value
p =Point(3,5)+Point(4,6)print(p.x, p.y) #出力-1,-1
__ add__
の特定の実装が減算として記述されている場合、このタイプのエラーを見つけるのは非常に困難です。したがって、サードパーティ用のライブラリを記述していない場合は、基本的に演算子のオーバーロードは必要ありません。
hash
を使用してオブジェクトのハッシュ値を計算すると、オブジェクトの __hash__
メソッドが呼び出されます。サンプルコードは次のとおりです。In [1]:classPoint:...: def __hash__(self):...:return1...:
In [2]:hash(Point())
Out[2]:1
__ hash__
メソッドはintを返す必要があります。そうでない場合、TypeErrorがスローされますIn [1]:classPoint:...: def __hash__(self):...:return'aaa'...:
In [2]:hash(Point())---------------------------------------------------------------------------
TypeError Traceback(most recent call last)<ipython-input-5-a919dcea3eae>in<module>()---->1hash(Point())
TypeError: __hash__ method should return an integer
__hash__
メソッドを持つオブジェクトです。In [6]:classPoint:...: def __hash__(self):...:return1...:
In [7]:set([Point(),12]) #ハッシュできます
Out[7]:{<__main__.Point at 0x7f19d4073320>,12}
In [8]: Point.__hash__ = None
In [9]:set([Point(),12]) #ハッシュできないため、コレクションに配置できません
---------------------------------------------------------------------------
TypeError Traceback(most recent call last)<ipython-input-10-25999920b521>in<module>()---->1set([Point(),12])
TypeError: unhashable type:'Point'
__hash__
メソッドをオーバーライドしない場合、このクラスの各オブジェクトは通常、異なるハッシュを持ちますIn [1]:classPoint:...: pass
...:
In [2]: p1 =Point()
In [3]: p2 =Point()
In [4]:hash(p1)
Out[4]:8757059543567
In [5]:hash(p2)
Out[5]:8757059543756
__hash__
は __eq__
と一緒に使用されます。これは、インタプリタが通常、ハッシュが等しいかどうか、インスタンスが等しいかどうかを判断するためですclassPoint:
def __init__(self, x, y):
self.x = x
self.y = y
def __hash__(self):returnhash('{}:{}'.format(self.x, self.y))
def __eq__(self, other):return self.x == other.x and self.y == other.y
p1 =Point(3,5)
p2 =Point(3,5)set([p1, p2]) #戻る{<__main__.Point at 0x7f286092d588>}hash(p1)==hash(p2) #Trueを返す
p1 == p2 #Trueを返す
オブジェクトが __len__
メソッドを実装している場合、組み込みメソッド len
を使用してオブジェクトの長さを見つけることができ、 __len__
メソッドは負でない整数を返す必要があります
lst =[1,2,3]len(lst) #3を返します
lst.__len__() #3を返します
したがって、組み込み関数と __len__
メソッドは同じ効果があります。
classSized:
def __len__(self):return10len(Sized()) #10を返します
__bool__
メソッドを実装する場合、bool(o)
の戻り値はo .__ bool __()
です。classF:
def __bool__(self):return False
bool(F()) #Falseを返す
classT:
def __bool__(self):return True
bool(T()) #Trueを返す
__bool__
メソッドを実装していない場合、oが __len__
メソッドを実装していると、 bool(o)
の戻り値は len(o)!= 0
になります。classL:
def __len__(self):return3bool(L()) #Trueを返す
classQ:
def __len__(self):return0bool(Q()) #Falseを返す
__bool__
メソッドも __len__
メソッドも実装していない場合、 bool(o)
の戻り値は True
です。classBoolean:
pass
bool(Boolean()) #Trueを返す
__ bool__
は __len__
よりも優先度が高いclassSized:
def __init__(self, size):
self.size = size
def __len__(self):return self.size
def __bool__(self):return self.size ==0bool(Sized(0)) #Trueを返す
bool(Sized(10)) #Falseを返す
__ bool__
メソッドはboolタイプを返す必要がありますclassB:
def __bool__(self):return None #非boolタイプの値を返すとエラーが発生し、intタイプが返された場合でも、エラーが報告されます。
bool(B())---------------------------------------------------------------------------
TypeError Traceback(most recent call last)<ipython-input-80-4efbb03885fe>in<module>()---->1bool(B())
TypeError: __bool__ should return bool, returned NoneType
__ str__
メソッドの場合、print関数は基本的に、オブジェクトの __str__
メソッドを呼び出して読み取ります。__ repr__
メソッド、repr関数は基本的に、マシンに読み取るためにオブジェクトの __repr__
メソッドを呼び出しますclassPoint:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self): #読むには
return'Point<{}, {}>'.format(self.x, self.y)
def __repr__(self): #機械可読
return'Point({}, {})'.format(self.x, self.y)print(Point(3,5)) # Point<3,5>print(repr(Point(3,5))) # Point(3,5)
repr:オブジェクトの正規化された文字列表現を返します
classFn:
def __call__(self):print('{} called'.format(self))
f =Fn()f()
# 出力
<__ main__.Fn object at 0x7fd254367470> called
オブジェクトは、 __call__
メソッドを実装している限り、括弧で呼び出すことができます。このタイプのオブジェクトは、呼び出し可能オブジェクトと呼ばれます。
オブジェクトに関数を追加するということは、 __call__
メソッドにパラメーターを追加することを意味します。
classAdd:
def __call__(self, x, y):return x + y
Add()(3,5) #8を返します。これはaddと同等です。=Add()add(3,5)
呼び出し可能オブジェクトのアプリケーション例:有効期限と交換可能なキャッシュデコレータを実装する
import inspect
import datetime
from functools import wraps
classCache:
def __init__(self, size=128, expire=0):
self.size = size
self.expire =0
self.data ={}
@ staticmethod
def make_key(fn, args, kwargs):
ret =[]
names =set()
params = inspect.signature(fn).parameters
keys =list(params.keys())for i, arg inenumerate(args):
ret.append((keys[i], arg))
names.add(keys[i])
ret.extend(kwargs.items())
names.update(kwargs.keys())for k, v in params.items():if k not in names:
ret.append((k, v.default))
ret.sort(key=lambda x: x[0])return'&'.join(['{}={}'.format(name, arg)for name, arg in ret])
def __call__(self, fn):
@ wraps(fn)
def wrap(*args,**kwargs):
key = self.make_key(fn, args, kwargs)
now = datetime.datetime.now().timestamp()if key in self.data.keys():
value, timestamp, _ = self.data[key]if expire ==0 or now - timestamp < expire:
self.data[key]=(value, timestamp, now)return value
else:
self.data.pop(key)
value =fn(*args,**kwargs)iflen(self.data)>= self.size:
# 期限切れのクリーンアップ
if self.expire !=0:
expires =set()for k,(_, timestamp, _)in self.data.items():if now - timestamp >= self.expire:
expires.add(k)for k in expires:
self.data.pop(k)iflen(self.data)>= self.size:
# 交換する
k =sorted(self.data.items(), key=lambda x: x[1][2])[0][0]
self.data.pop(k)
self.data[key]=(value, now, now)return value
return wrap
@ Cache()
def add(x, y):return x + y
add(1,2) #3を返します
__call__
を使用して呼び出し可能なオブジェクトを実装します。クロージャーには同じ目標があり、通常は内部状態をカプセル化します。
classContext:
def __enter__(self):print('enter context')
def __exit__(self,*args,**kwargs):print('exit context')
オブジェクトが __enter__
メソッドと __exit__
メソッドの両方を実装する場合、このオブジェクトはコンテキスト管理をサポートするオブジェクトです。
コンテキスト管理をサポートするオブジェクトは、次のステートメントブロックを使用して処理できます。
with obj:
pass
といった
withContext():print('do somethings')print('out of context')
# 出力
enter context
do somethings
exit context
out of context
したがって、 with
はステートメントブロックを開き、このステートメントブロックを実行する前に、 __enter__
メソッドが実行され、このステートメントブロックを実行した後、 __exit__
メソッドが実行されます。つまり、このステートメントブロックの前後にいくつかの操作が実行されます。したがって、コンテキストとも呼ばれます。
__enter__
と __exit__
が実行されるため、コンテキスト管理は安全です。 ****withContext():
raise Exception()
enter context
exit context
---------------------------------------------------------------------------
Exception Traceback(most recent call last)<ipython-input-126-c1afee4bfdab>in<module>()1withContext():---->2 raise Exception()
Exception:
with
ブロックでアクティブに終了した場合でも、 __enter__
と __exit__
は実行できますimport sys
withContext():
sys.exit()
enter context
exit context
An exception has occurred, use %tb to see the full traceback.
SystemExit
/home/clg/.pyenv/versions/3.5.2/envs/normal/lib/python3.5/site-packages/IPython/core/interactiveshell.py:2889: UserWarning: To exit: use 'exit','quit', or Ctrl-D.warn("To exit: use 'exit', 'quit', or Ctrl-D.", stacklevel=1)
as句は
enter`メソッドの戻り値を取得できますclassContext:
def __enter__(self):print('enter context')return self # __enter__関数の戻り値
def __exit__(self,*args,**kwargs):print('exit context')
ctx =Context()with ctx as c:print(id(ctx))print(id(c))print(c)
# 出力結果
enter context
140541332713712140541332713712<__ main__.Context object at 0x7fd2543670f0>
exit context
__ enter__
method__ enter__
メソッドの戻り値は、as句で取得できます__ enter__
はself **以外のパラメータを取りませんclassContext:
def __enter__(self,*args,**kwargs):print('enter context')print(args)print(kwargs)
def __exit__(self,*args,**kwargs):print('exit context')
# 出力
enter context(){}
exit context
argsとkwargsはどちらも空であるため、 __enter__
関数は、コンテキスト管理中にself以外のパラメーターを取りません。
__ exit__
method__ exit__
の戻り値を取得する方法はありません。__exit__
がFalseを返したときに with
ブロックで例外がスローされた場合、例外は上向きにスローされ、Trueが返され、例外はシールドされますclassContext:
def __enter__(self):print('enter context')
def __exit__(self,*args,**kwargs):print('exit context')return'haha'withContext()as c:print(c)
# 出力
enter context
None
exit context
__ exit__
、exception、traceback **の3つのパラメーター例外タイプclassContext:
def __enter__(self):print('enter context')
def __exit__(self,*args,**kwargs):print('exit context')print(args)print(kwargs)withContext():
pass
# 出力
enter context
exit context(None, None, None){}
argsは3つのNoneを出力します。これは3つの位置パラメーターを意味し、kwargsは空です。これはキーワード引数がないことを意味します。
withContext():
raise Exception()
enter context
exit context(<class'Exception'>,Exception(),<traceback object at 0x7f28608fdc88>){}---------------------------------------------------------------------------
Exception Traceback(most recent call last)<ipython-input-145-c1afee4bfdab>in<module>()1withContext():---->2 raise Exception()
Exception:
__exit__
の3つのパラメーター、exc_type、exc_value、traceback **を受け入れます。classContext:
def __enter__(self):print('enter context')
def __exit__(self, exc_type, exc_value, traceback):print('exit context')print('exception type: {}'.format(exc_type))print('exception value: {}'.format(exc_value))print('exception traceback: {}'.format(traceback))return True
withContext():
raise TypeError('hahaha')
# 出力
enter context
exit context
exception type:<class'TypeError'>
exception value: hahaha
exception traceback:<traceback object at 0x7fd257c18608>
withステートメントは、リソースにアクセスして、使用中に例外が発生したかどうかに関係なく、必要な「クリーンアップ」操作が実行され、使用後のファイルの自動クローズ、スレッドのロックの自動取得と解放などのリソースが解放されるようにするのに適しています。つまり、コードブロックの前後にコードが挿入されるすべてのシナリオが適用可能です**
以下の例としてタイマーを取り上げます
from functools import wraps
classTimeit:
def __init__(self, fn=None):wraps(fn)(self)
def __call__(self,*args,**kwargs):
start = datetime.datetime.now()
ret = self.__wrapped__(*args,**kwargs)
cost = datetime.datetime.now()- start
print(cost)return ret
def __enter__(self):
self.start = datetime.datetime.now()
def __exit__(self,*args):
cost = datetime.datetime.now()- self.start
print(cost)withTimeit():
z =3+8 #出力0:00:00.000037
@ Timeit
def add(x, y):return x + y
add(3,8) #出力0:00:00.000044は11を返します
合計2つのタイミング方法が実装されており、ステートメントブロックまたは関数の時間を計るために使用できます。
Contextlibは、よりも美しいものであり、コンテキスト管理メカニズムを提供するモジュールでもあります。これは、 __enter__
や __exit__
を使用する代わりに、ジェネレータデコレータを介して実装されます。 contextlibのcontextmanagerは、関数レベルのコンテキスト管理メカニズムを提供するデコレータとして機能します。
import contextlib
@ contextlib.contextmanager
def context():print('enter context') #初期化部分はと同等です__enter__方法
try:yield'haha' #に相当__enter__戻り値
finally:print('exit context') #パーツをクリーンアップします。__exit__方法
withcontext()as c:print(c)
raise Exception()
# 出力
enter context
haha
exit context
---------------------------------------------------------------------------
Exception Traceback(most recent call last)<ipython-input-189-4c1dae6b647a>in<module>()1withcontext()as c:2print(c)---->3 raise Exception()
Exception:
降伏後、finallyと一緒に使用する必要があります。そうしないと、例外がスローされた場合、プログラムは降伏後に部門を実行しません。つまり、 __exit__
の一部は実行されません。
**Pythonの反省、コアエッセンスは実際には文字列の形式を使用してオブジェクト(モジュール)のメンバーを操作(検索/取得/削除/追加)することです。これは文字列ベースのイベント駆動型です! ****
モジュールのpythonリフレクションおよびリフレクションメカニズムの分析については、[pythonリフレクションメカニズムの詳細な分析](https://www.cnblogs.com/feixuelove1009/p/5576206.html)を参照してください。
以下は主にクラスオブジェクトの反射メカニズムを分析します
3つの機能のプロトタイプ:
主な機能は、オブジェクトのメンバー名でオブジェクトのメンバーを取得することです
classPoint:
def __init__(self, x, y):
self.x = x
self.y = y
def print(self, x, y):print(x, y)
p =Point(3,5)
p.__dict__['x'] #戻り値3。属性については、渡すことができます__dict__入手します
getattr(p,'print')(3,5) #メンバーメソッドが失敗しました__dict__取得されますが、getattr関数を介して取得できます# p.print(3,5)getattr(p,'x') #getattrrは属性も取得できます
setattr(p,'haha','abcd') # p.haha ='abcd'、オブジェクトpに属性hahaを追加します
p.haha #abcdを返す
hasattr(p,'print') #Trueを返す
setattrのオブジェクトはインスタンスです。インスタンスにメソッドを動的に追加する場合は、最初に関数をメソッドに変換する必要があります。変換方法は次のとおりです。
import types
def mm(self):print(self.x)setattr(p,'mm', types.MethodType(mm, p)) #mm関数をオブジェクトpのメソッドに変換した後、pを追加します
p.mm() #出力3
getattr setattr hasattrを使用して、コマンドルーターを実装します。
classCommand:
def cmd1(self):print('cmd1')
def cmd2(self):print('cmd2')
def run(self):while True:
cmd =input('>>>').strip()if cmd =='quit':returngetattr(self, cmd, lambda :print('not found cmd {}'.format(cmd)))()
command =Command()
command.run()
# 出力
>>> cmd1
cmd1
>>> cmd2
cmd2
>>> cmd3
not found cmd cmd3
>>> quit
__ getattr__``__setattr__``__delattr__
__getattr__
メソッドを定義するときに、存在しないメンバーにアクセスすると、 __getattr__
メソッドが呼び出されます。classA:
def __init__(self):
self.x =3
a =A()
a.x #3を返します
a.y #実現しない場合__getattr__メソッド、存在しないメンバーにアクセスするとエラーが報告されます
---------------------------------------------------------------------------
AttributeError Traceback(most recent call last)<ipython-input-228-cc7049c6eeec>in<module>()---->1 a.y
AttributeError:'A' object has no attribute 'y'
__getattr__
メソッドを追加します
classA:
def __init__(self):
self.x =3
def __getattr__(self, name):return'missing property {}'.format(name)
a =A()
a.x #3を返します
a.y #戻る'missing property y'。つまり、存在しないメンバーにアクセスするには、__getattr__方法
__setattr__
を実装する場合、このクラスのオブジェクトに属性を追加したり、既存の属性に値を割り当てたりする場所は、 __setattr__
を呼び出します。classA:
def __init__(self):
self.x =3
def __setattr__(self, name, value):print('set {} to {}'.format(name, value))setattr(self, name, value)
a =A()
a.x #3を返します
a.y =5 #出力セットyを5
__delattr__
メソッドを実装し、そのインスタンスの属性を削除すると、このメソッドが呼び出されますclassA:
def __init__(self):
self.x =3
def __delattr__(self, name):print('you cannot delete property: {}'.format(name))
a =A()
a.x #3を返します
del a.x #プロパティを削除できない出力: x
Recommended Posts