Pythonオブジェクトの属性アクセスプロセスの詳細な説明

1つの質問に答えたいだけです:コンパイラがobj.fieldを読みたいときに何が起こったのですか?

一見単純なプロパティアクセスであるため、プロセスは非常に複雑です。次の手順が合計されます。

  1. obj自体(インスタンス)にこの属性がある場合は戻ります。ない場合は、手順2を実行します。

  2. objのクラスにこの属性がある場合は、戻ります。ない場合は、手順3に進みます。

  3. objクラスの親クラスにこの属性がある場合は、戻ります。ない場合は、すべての親クラスにアクセスするまで3を実行し続けます。まだ存在しない場合は、手順4を実行します。

  4. obj .__ getattr__メソッドを実行します。

次のコードで確認できます:

classA(object):
 a ='a'classB(A):
 b ='b'classC(B):
 class_field ='class field'
 def __getattr__(self, f):print('Method {}.__getattr__ has been called.'.format(
  self.__class__.__name__))return f
c =C()
print c.a
print c.b
print c.class_field
print c.c

出力:

a
b
classfield
Method C.__getattr__ has been called.
c

PS:pythonの属性はプロパティとは異なります。プロパティを使用する場合、プロパティの解析優先度が最も高くなります。詳細については、ブログ「属性からプロパティへ」を参照してください。

補足知識:pythonオブジェクトと属性の深い理解

クラス属性とインスタンス属性

まず、クラス属性とクラスインスタンス属性がどのようにpythonに保存されているかを見てみましょう。__dir__メソッドを使用してオブジェクト属性を表示します

classTest(object):
 pass
 test =Test()
# クラス属性の表示
 dir(Test)['__class__','__delattr__','__dict__','__doc__','__format__','__getattribute__','__hash__','__init__','__module__','__new__','__reduce__','__reduce_ex__','__repr__','__setattr__','__sizeof__','__str__','__subclasshook__','__weakref__']
# インスタンスのプロパティを表示する
 dir(test)['__class__','__delattr__','__dict__','__doc__','__format__','__getattribute__','__hash__','__init__','__module__','__new__','__reduce__','__reduce_ex__','__repr__','__setattr__','__sizeof__','__str__','__subclasshook__','__weakref__']

__dict__はオブジェクトの属性を保存するため、主に属性__dict__を確認します。次の例を参照してください。

classSpring(object):...   season ="the spring of class"... 

# Springクラスによって保存されたプロパティを表示する
 Spring.__dict__
dict_proxy({'__dict__':<attribute '__dict__'of'Spring' objects ,'season':'the spring of class','__module__':'__main__','__weakref__':<attribute '__weakref__'of'Spring' objects ,'__doc__': None})

# 2つの方法でクラス属性にアクセスする
 Spring.__dict__['season']'the spring of class'
 Spring.season
' the spring of class'

__dict__には、このクラスの属性である「シーズン」キーがあり、その値はクラス属性のデータであることがわかります。

インスタンスのプロパティを見てみましょう

 s =Spring()
# インスタンス属性__dict__空です
 s.__dict__
{}
# 実際にはクラス属性が指している
 s.season
' the spring of class'

# インスタンスプロパティを作成する
 s.season ="the spring of instance"
# このように、インスタンスのプロパティは空ではありません。この時点で作成されたインスタンス属性とクラス属性は同じ名前で上書きされます
 s.__dict__
{' season':'the spring of instance'}
 s.__dict__['season']'the spring of instance'
 s.season
' the spring of instance'

# クラス属性はインスタンス属性の影響を受けません
 Spring.__dict__['season']'the spring of class'
 Spring.__dict__
dict_proxy({'__dict__':<attribute '__dict__'of'Spring' objects ,'season':'the spring of class','__module__':'__main__','__weakref__':<attribute '__weakref__'of'Spring' objects ,'__doc__': None})

# インスタンス属性が削除された場合,クラス属性
 del s.season
 s.__dict__
{}
 s.season
' the spring of class'

# カスタムインスタンス属性はクラス属性に影響を与えません
 s.lang ="python"
 s.__dict__
{' lang':'python'}
 s.__dict__['lang']'python'

# クラス属性の変更
 Spring.flower ="peach"
 Spring.__dict__
dict_proxy({'__module__':'__main__','flower':'peach','season':'the spring of class','__dict__':<attribute '__dict__'of'Spring' objects ,'__weakref__':<attribute '__weakref__'of'Spring' objects ,'__doc__': None})
 Spring.__dict__['flower']'peach'
# インスタンスでは__dict__変化なし
 s.__dict__
{' lang':'python'}
# インスタンスでflower属性が見つかりません。class属性を呼び出してください
 s.flower
' peach'

クラスに含まれるメソッドと、__ dict__がどのように変化するかを見てみましょう

# 定義クラス
 classSpring(object):...   def tree(self, x):...     self.x = x
... return self.x
... 
# のメソッドツリー__dict__内部
 Spring.__dict__
dict_proxy({'__dict__':<attribute '__dict__'of'Spring' objects ,'__weakref__':<attribute '__weakref__'of'Spring' objects ,'__module__':'__main__','tree':<function tree at 0xb748fdf4,'__doc__': None})
 Spring.__dict__['tree']<function tree at 0xb748fdf4 

# インスタンスを作成しますが__dict__に方法はありません
 t =Spring()
 t.__dict__
{}

# 実行方法
 t.tree("xiangzhangshu")'xiangzhangshu'
# インスタンス方式(t.tree('xiangzhangshu'))最初のパラメータ(自己、しかしそれを書きませんでした)自己を介してインスタンスtをバインドする.xで値を設定します。つまり、tを指定します。.__dict__属性値を追加します。
 t.__dict__
{' x':'xiangzhangshu'}
# xがselfの属性に割り当てられていないが、直接戻る場合、結果は変更されています
 classSpring(object):...   def tree(self, x):...return x
 s =Spring()
 s.tree("liushu")'liushu'
 s.__dict__
{}

pythonの観点を理解する必要があります。クラスであろうとインスタンスであろうと、すべてがオブジェクトであり、object.attributeに準拠し、独自の属性を持つオブジェクトと見なすことができます。

__slots__を使用してメモリ使用量を最適化します

デフォルトでは、pythonはインスタンス属性を各インスタンスの__dict__という名前の辞書に格納し、辞書はタプルの__slots__クラス属性を介して大量のメモリを消費します(辞書はアクセス速度を向上させるために基になるハッシュテーブルを使用する必要があります)インスタンス属性は辞書なしで保存されるため、多くのメモリを節約できます

# クラスで定義__slots__属性とは、このクラスのすべてのインスタンスの属性がここにあることを意味します。数百万のインスタンスが同時にアクティブになると、多くのメモリを節約できます。
 classSpring(object):...   __slots__ =("tree","flower")... 
# dirを詳しく見てください()結果、そして__dict__属性?もう、もう。言い換えると__slots__置く__dict__絞り出されて、クラスの属性を入力しました。
 dir(Spring)['__class__','__delattr__','__doc__','__format__','__getattribute__','__hash__','__init__','__module__','__new__','__reduce__','__reduce_ex__','__repr__','__setattr__','__sizeof__','__slots__','__str__','__subclasshook__','flower','tree']
 Spring.__slots__('tree','flower')
# インスタンス化
 t =Spring()
 t.__slots__('tree','flower')

# クラスごとに属性値を割り当てる
 Spring.tree ="liushu"
# ツリー属性は読み取り専用です,インスタンスは変更できません
 t.tree ="guangyulan"Traceback(most recent call last):
 File "<stdin ", line 1,in<module 
AttributeError:'Spring' object attribute 'tree' is read-only
 t.tree
' liushu'

# クラス属性が割り当てられた属性の場合、それらは変更にのみ使用できます
 Spring.tree ="guangyulan"
 t.tree
' guangyulan'

# クラス属性が割り当てられていない属性については、例を使用して変更できます
 t.flower ="haitanghua"
 t.flower
' haitanghua'
# インスタンス属性の値はクラス属性に戻されません。同じ名前で新しく作成されたインスタンス属性として理解することもできます。
 Spring.flower
< member 'flower'of'Spring' objects 
# クラス属性に値を割り当てる場合
 Spring.flower ="ziteng"
 t.flower
' ziteng'

__slots__を適切に使用すると、メモリを大幅に節約できます。必要に応じて問題に注意してください

クラスで__slots__を定義した後、インスタンスは__slots__にリストされている名前以外の属性を持つことはできません。

インタプリタは継承された__slots__属性を無視するため、すべてのサブクラスは__slots__を使い慣れるように定義する必要があります

__werkref__が__slots__に追加されていない場合、インスタンスを弱い参照のターゲットにすることはできません

プロパティの魔法の方法

いくつかの魔法の方法を見てください

__ setattr__(self,name,value):nameに値を割り当てる場合は、このメソッドを呼び出します。
__ getattr__(self,name):nameにアクセスし、それが存在しない場合、このメソッドが呼び出されます。
__ getattribute__(self,name):名前にアクセスすると自動的に呼び出されます(注:これは新しいスタイルのクラスにのみ使用できます)。名前が存在するかどうかに関係なく、呼び出されます。
__ delattr__(self,name):名前を削除したい場合は、このメソッドが呼び出されます。
 classA(object):...   def __getattr__(self, name):...     print "You use getattr"...   def __setattr__(self, name, value):...     print "You use setattr"...     self.__dict__[name]= value
# a.x、このセクションの冒頭の例によれば、エラーが報告されます。ただし、__getattr__(self, name)オブジェクトのにxが存在しないことが判明した場合のメソッド__dict__それが入っているとき、それは呼ばれます__getattr__、いわゆる「インターセプトメンバー」。
 a =A()
 a.x
You use getattr

# オブジェクトのプロパティに値を割り当てるときは、__setattr__(self, name, value)メソッド、このメソッドには文自己があります.__dict__[name]=値、このステートメントを介して、属性とデータがオブジェクトに保存されます__dict__に
 a.x =7
You use setattr

# テスト__getattribute__(self,name)classB(object):...   def __getattribute__(self, name):...     print "you are useing getattribute"...return object.__getattribute__(self, name)
# 返されたコンテンツはリターンオブジェクトを使用します.__getattribute__(self, name)リターンセルフを使用せずに.__dict__[name]。この方法を使うなら、それは自分自身を訪問することだからです.__dict__、このプロパティにアクセスする限り、電話する必要があります`getattribute``、これは無限の再帰につながります

# 存在しないメンバーを訪問すると、彼らがされていることがわかります__getattribute__傍受されましたが、最終的にはエラーが報告されます。
 b =B()
 b.y
you are useing getattribute
Traceback(most recent call last):
 File "<stdin ", line 1,in<module 
 File "<stdin ", line 4,in __getattribute__
AttributeError:'B' object has no attribute 'y'

プロパティ関数

porpertyは、メソッドを機能としてマークするためのデコレータとして使用できます

classVector(object):
 def __init__(self, x, y):
 # 2つの先頭の下線を使用して、属性をプライベートとしてマークします
 self.__x =float(x)
 self.__y =float(y)
  
 # ポルノデコレータは、読み取り方法を機能としてマークします
 @ property
 def x(self):return self.__x
    
 @ property
 def y(self):return self.__y
    
vector =Vector(3,4)print(vector.x, vector.y)

プロパティを使用して関数をプロパティとしてカプセル化します

classRectangle(object):"""
 the width and length of Rectangle
  """
 def __init__(self):
 self.width =0
 self.length =0

 def setSize(self, size):
 self.width, self.length = size
 def getSize(self):return self.width, self.length

if __name__ =="__main__":
 r =Rectangle()
 r.width =3
 r.length =4
 print r.getSize()  # (3,4)
 r.setSize((30,40))
 print r.width  # 30
 print r.length  # 40

このコードは正常に実行できますが、プロパティの呼び出し方法を次のように改善できます。

classRectangle(object):"""
 the width and length of Rectangle
  """
 def __init__(self):
 self.width =0
 self.length =0

 def setSize(self, size):
 self.width, self.length = size
 def getSize(self):return self.width, self.length
 # プロパティメソッドを使用して、関数を属性としてカプセル化します。これは、よりエレガントです。
 size =property(getSize, setSize)if __name__ =="__main__":
 r =Rectangle()
 r.width =3
 r.length =4
 print r.size   # (30,40)
 r.size =30,40
 print r.width  # 30
 print r.length  # 40

魔法の方法を使用して以下を達成します:

classNewRectangle(object):
 def __init__(self):
 self.width =0
 self.length =0
  
 def __setattr__(self, name, value):if name =='size':
  self.width, self, length = value
 else:
  self.__dict__[name]= value
      
 def __getattr__(self, name):if name =='size':return self.width, self.length
 else:
  raise AttrubuteErrir
      
if __name__ =="__main__":
 r =Rectangle()
 r.width =3
 r.length =4
 print r.size   # (30,40)
 r.size =30,40
 print r.width  # 30
 print r.length  # 40

属性取得順序

最後に、おなじみの取得シーケンスを見てみましょう。インスタンスを介してその属性を取得し、__ dict__に対応する属性がある場合は、結果を直接返します。ない場合は、クラス属性で見つけます。

次の例を考えてみましょう。

classA(object):
 author ="qiwsir"
 def __getattr__(self, name):if name !="author":return"from starter to master."if __name__ =="__main__":
 a =A()
 print a.author # qiwsir
 print a.lang # from starter to master.

a = A()の場合、インスタンスの属性は作成されないか、インスタンスの__dict__が空になります。ただし、a.authorを確認する場合は、インスタンスの属性がないため、クラスの属性でa.authorを検索して存在することを確認すると、値「qiwsir」が返されます。しかし、私がa.langを探していたとき、それはインスタンス属性だけでなくクラス属性にもあったので、__ getattr ()メソッドが呼び出されました。上記のクラスにはこのメソッドがありますが、 getattr __()メソッドがない場合はどうなりますか?このメソッドが定義されていない場合、以前に見られたAttributeErrorが発生します。

上記のPythonオブジェクトの属性アクセスプロセスの詳細な説明は、エディターによって共有されるすべてのコンテンツです。参照を提供したいと思います。

Recommended Posts

Pythonオブジェクトの属性アクセスプロセスの詳細な説明
Python super()メソッドの原理の詳細な説明
Pythondecimalモジュールの使用法の詳細な説明
Python関数パラメータ分類の原理の詳細な説明
Pythonタイマースレッドプールの原理の詳細な説明
Pythonインターフェース開発の実装手順の詳細な説明
Pythonプロセス制御の一般的なツールの詳細な説明
pythonに基づく残りの問題の詳細な説明(%)
pythonバックトラッキングテンプレートの詳細な説明
pythonシーケンスタイプの詳細な説明
python accesshdfsの操作
PythonIOポート多重化の詳細な説明
属性からプロパティまでのPython詳細な説明
pythonコマンドの-uパラメーターの詳細な説明
Python推測アルゴリズムの問題の詳細な説明
Pythonでのpipの使用に関する詳細な説明|サードパーティライブラリのインストールの概要
python標準ライブラリOSモジュールの詳細な説明
Pythonに基づくデータタイプの詳細な説明
PythonWebページパーサーの使用例の詳細な説明
Pythonの基盤を統合する(4)
Python(7)の基盤を統合する
Python(6)の基盤を統合する
Python(5)の基盤を統合する
Pythonの基盤を統合する(3)
pythonでのwheelの使用法
Pythonプラグインメカニズムの詳細な実装
Pythonは中国語の4つの車輪を処理します
地主取引のPythonシミュレーション
Pythonの用途は何ですか
Ubuntu環境でのSSHのインストールと使用の詳細な説明
Pythonでの辞書の詳細な使用法
pythonアクセスAlipayの操作例
Python文字列プーリングの前提
Python3.8の新機能の秘密
Pythonの父がMicrosoftに加わる
pythonでのタプルの使用法
pythonを実行するメソッドを終了します
Pythonはスタックの構造を実装できますか?
pythonインタラクティブモードの基本を学ぶ
pythonの必須パラメーターは何ですか
pythonの下部にあるロジスティック回帰
Python3クローラーでのAjaxの使用
PythonはTowerofHanoiゲームを解決します
pythonの複数のバージョンの競合を解決します
python変数の範囲は何ですか
Pythonは分数シーケンスの合計を実装します
Pythonガベージコレクションメカニズムの詳細な分析
Pythonの基礎を学ぶ2日間
pythonのid関数は何ですか
Python言語の本質:Itertoolsライブラリ
python言語の利点は何ですか
pythonインスタンス化オブジェクトの特定のメソッド
python3はマスク描画の機能を実現します
python開発の見通しは何ですか
Python仮想環境venvの使用法の詳細な説明
pythonの関数本体は何ですか
[Centos8]ドッカーをインストールするでこぼこのプロセス
pythonインポートライブラリの特定の方法
pythonの複数のバージョンの競合を解決します
pythonでのadbの機能は何ですか
Ubuntu20.04インストールPython3仮想環境チュートリアル詳細な説明