Pythonオブジェクト指向の魔法の方法

魔法の方法#

クラスの魔法のメソッドを見る

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__

作成/破棄#

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

In [1]:classPoint:...:     def __hash__(self):...:return1...:     

In [2]:hash(Point())
Out[2]:1
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
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'
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
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

classF:
 def __bool__(self):return False

bool(F())  #Falseを返す

classT:
 def __bool__(self):return True

bool(T())  #Trueを返す
classL:
 def __len__(self):return3bool(L())  #Trueを返す

classQ:
 def __len__(self):return0bool(Q())  #Falseを返す
classBoolean:
 pass

bool(Boolean())  #Trueを返す
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を返す
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

視覚化#

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__メソッドが実行されます。つまり、このステートメントブロックの前後にいくつかの操作が実行されます。したがって、コンテキストとも呼ばれます。

withContext():
 raise Exception()

enter context
exit context
---------------------------------------------------------------------------
Exception                                 Traceback(most recent call last)<ipython-input-126-c1afee4bfdab>in<module>()1withContext():---->2     raise Exception()

Exception:
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)

ワードとしてのブロック付き##

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

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

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
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:
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ステートメントは、リソースにアクセスして、使用中に例外が発生したかどうかに関係なく、必要な「クリーンアップ」操作が実行され、使用後のファイルの自動クローズ、スレッドのロックの自動取得と解放などのリソースが解放されるようにするのに適しています。つまり、コードブロックの前後にコードが挿入されるすべてのシナリオが適用可能です**

  1. 資源管理
  2. ASD

以下の例としてタイマーを取り上げます

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つのタイミング方法が実装されており、ステートメントブロックまたは関数の時間を計るために使用できます。

contextmanagerの使用##

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)を参照してください。

以下は主にクラスオブジェクトの反射メカニズムを分析します

getattr setattr hasattr

3つの機能のプロトタイプ:

  1. getattr:getattr(object、name [、default])->値。 getattr(x、 'y')はxyと同等です
  2. setattr:setattr(obj、name、value、/)。 setattr(x、 'y'、v)はxy = vと同等です
  3. hasattr:hasattr(obj, name, /)

主な機能は、オブジェクトのメンバー名でオブジェクトのメンバーを取得することです

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__

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__方法
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
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

Pythonオブジェクト指向の魔法の方法
Pythonマジックメソッドのトピック
Pythonオブジェクト指向の例
Pythonオブジェクト指向の基本
Python3.7デバッグサンプルメソッド
Python関数-辞書get()メソッド
Pythonエラー処理方法
パイソンブラックマジックメタクラス
Pythonは関数メソッドを定義します
PythonTCPパケットインジェクション方式
Python描画リンググラフ法
情報メソッドを除くPython追跡
Pythonマジック関数eval()学習
Pythonで一般的に使用される魔法の方法
Pythonオブジェクト指向プログラミングの分析
Pythonは勾配降下法を実装しています
pythonのオブジェクト指向とは何ですか
Pythonの右揃えの例の方法
パラメータを渡すPythonメソッド
Python算術シーケンス計算方法
pythonフォントを増やす方法の手順
Pythonの対数法の要約
Pythonとjsのインタラクティブな呼び出しメソッド
魔法の方法とPythonの使用
Pythonは最も急な降下方法を実装します
pythonオブジェクト指向プログラミングを理解する方法
pythonを実行するメソッドを終了します
1分でPythonを学ぶ|オブジェクト指向(中国語)
pythonにdjangoモジュールをインストールする方法
pythonは勾配メソッドを実装しますpython最速の降下メソッド
pythonインスタンス化オブジェクトの特定のメソッド
不可欠な例を見つけるためのPythonRombergメソッド
pythonからsqlを読み取る方法の例
音楽ファイルを開くためのPythonのサンプルメソッド
pythonインポートライブラリの特定の方法
Python置換ピップソースメソッドプロセス分析