43. 記述子
#43. 記述子
記述子は、別のオブジェクトの属性アクセスを制御するオブジェクトです。記述子は、Python のオブジェクト モデルの背後にある主要なメカニズムの 1 つです。メソッドがインスタンスにどのようにバインドされるかについて説明します。property動作、方法staticmethodそしてclassmethod動作、スロットの動作、および Python レベルの構文に接続する CPython レベルの型操作の数。
言語レベルでは、記述子は次のメソッドの 1 つ以上を定義するオブジェクトです。python id="x7hcrg" __get__(self, obj, objtype=None) __set__(self, obj, value) __delete__(self, obj) オブジェクト__get__記述子です。
オブジェクト__set__または__delete__データ記述子です。
非データ記述子とデータ記述子の区別により、検索の優先順位が制御されます。
43.1 記述子が存在する理由
Python の属性アクセスは簡単に見えます。python id="96l8i5" obj.name ただし、この式は「という名前のフィールドを読み取る」という意味ではありません。name記憶から。」
それは次のことを意味します:```text id="drqc0j" ask the object's type how attribute lookup works search descriptors and dictionaries in a defined order possibly call descriptor methods return the resulting object
例:```python id="tkau0z"
obj.method
obj.property_name
Class.class_method
Class.static_method
obj.slot_name
```これらはすべて記述子の動作に関係します。
## 43.2 基本的な記述子プロトコル
記述子は以下を定義します`__get__`。```python id="ct8qxy"
class Descriptor:
def __get__(self, obj, objtype=None):
return "computed value"
```これをクラス属性として使用します。```python id="d0fypg"
class Example:
value = Descriptor()
e = Example()
print(e.value)
```出力:```text id="qz34l2"
computed value
```記述子オブジェクトはクラスに保存されます。```python id="csuohy"
print(Example.__dict__["value"])
```ただし、インスタンス呼び出しを介した属性アクセス`Descriptor.__get__`。
## 43.3 記述子の引数
記述子メソッドは以下を受け取ります。
|引数 |意味 |
|---|---|
|`self`|記述子オブジェクト |
|`obj`|アクセスされているインスタンス、または`None`クラスアクセス用 |
|`objtype`|所有者クラス |
例:```python id="v4lvjg"
class Descriptor:
def __get__(self, obj, objtype=None):
print("obj:", obj)
print("objtype:", objtype)
return 42
class Example:
value = Descriptor()
e = Example()
print(e.value)
print(Example.value)
```たとえばアクセス:```text id="by7jq8"
obj: <__main__.Example object at ...>
objtype: <class '__main__.Example'>
```クラスアクセスの場合:```text id="k4ktgr"
obj: None
objtype: <class '__main__.Example'>
```記述子はこの違いを利用して、インスタンスとクラスのアクセスに異なる値を返すことができます。
## 43.4 非データ記述子
非データ記述子は以下を定義します`__get__`、しかしそうではありません`__set__`または`__delete__`。```python id="hb1iji"
class NonDataDescriptor:
def __get__(self, obj, objtype=None):
return "descriptor value"
```例:```python id="bvccxu"
class Example:
value = NonDataDescriptor()
e = Example()
print(e.value)
```出力:```text id="j61t3h"
descriptor value
```ただし、これは非データ記述子であるため、インスタンス ディクショナリ エントリでオーバーライドできます。```python id="vu3t3a"
e.__dict__["value"] = "instance value"
print(e.value)
```出力:```text id="6xj4jn"
instance value
```この優先順位は意図的なものです。これは、通常のメソッドをインスタンス属性によってシャドウする方法です。
## 43.5 データ記述子
データ記述子は以下を定義します`__set__`または`__delete__`。```python id="rk5sgr"
class DataDescriptor:
def __get__(self, obj, objtype=None):
return "descriptor value"
def __set__(self, obj, value):
print("setting", value)
```例:```python id="c9rzsd"
class Example:
value = DataDescriptor()
e = Example()
e.__dict__["value"] = "instance value"
print(e.value)
```出力:```text id="m34bnr"
descriptor value
```データ記述子はインスタンス ディクショナリよりも優先されます。
このルールは次の場合に重要です`property`。
## 43.6 ルックアップの優先順位
通常のインスタンス属性アクセスの場合:```python id="ynllax"
obj.name
```CPython のオブジェクト検索は、おおよそ次の順序に従います。```text id="rpb90z"
1. Look for name on the type or base types.
2. If found and it is a data descriptor, call its __get__.
3. Look in the instance dictionary.
4. If found on the type and it is a non-data descriptor, call its __get__.
5. If found on the type as a normal attribute, return it.
6. If not found, call __getattr__ if defined.
7. Otherwise raise AttributeError.
```この順序は、一部のクラス属性がインスタンス属性によってシャドウできる理由と、他のクラス属性ができない理由を説明しています。
## 43.7 記述子の優先順位の例```python id="n4y91b"
class NonData:
def __get__(self, obj, objtype=None):
return "non-data descriptor"
class Data:
def __get__(self, obj, objtype=None):
return "data descriptor"
def __set__(self, obj, value):
raise AttributeError("read-only")
class Example:
a = NonData()
b = Data()
e = Example()
e.__dict__["a"] = "instance a"
e.__dict__["b"] = "instance b"
print(e.a)
print(e.b)
```出力:```text id="0dzdun"
instance a
data descriptor
a非データ記述子であるため、シャドウされます。bはデータ記述子であるためシャドウされません。
43.8 関数は記述子です
クラスに格納される関数は記述子です。```python id="4040yq" class Example: def method(self): return 42
e = Example()
print(Example.dict["method"]) print(e.method)
インスタンス アクセスによって返されるオブジェクトはバインドされたメソッドです。```text id="j0sg2u"
function object stored on class
↓ __get__(instance, class)
bound method object
```その理由は次のとおりです。```python id="r1yoxy"
e.method()
```パスする`e`最初の引数として。
## 43.9 メソッドバインディング
関数の記述子の動作は次のようにモデル化できます。```python id="rrmwg4"
class FunctionLike:
def __get__(self, obj, objtype=None):
if obj is None:
return self
return BoundMethod(self, obj)
```バインドされたメソッドには以下が格納されます。```text id="9ac6b1"
function
instance
```バインドされたメソッドを呼び出す:```python id="zs0r2l"
e.method(1, 2)
```は次とほぼ同等です:```python id="0jmqjg"
Example.method(e, 1, 2)
```これはパーサーの特別な構文ではありません。これは、通常の属性ルックアップ、記述子バインディング、および呼び出しです。
## 43.10 クラスからメソッドへのアクセス
クラスを通じてアクセスされると、関数記述子は次のメッセージを受け取ります。`obj=None`。```python id="c1o0cb"
class Example:
def method(self):
return 42
print(Example.method)
```結果は、インスタンスにバインドされたメソッドではなく、元の関数のようなオブジェクトになります。
したがって、これは機能します:```python id="ce7spz"
e = Example()
print(Example.method(e))
```インスタンスは明示的に渡されます。
## 43.11 バインドされたメソッドオブジェクト
バインドされたメソッド オブジェクトは、便利な属性を公開します。```python id="0hd310"
class Example:
def method(self):
return 42
e = Example()
m = e.method
print(m.__self__)
print(m.__func__)
__self__バインドされたインスタンスです。__func__基礎となる関数です。
概念的には:text id="9rhbma" bound_method.__self__ -> e bound_method.__func__ -> Example.__dict__["method"] 電話をかける:python id="r7sscl" m() 呼び出し:python id="sqt0vq" m.__func__(m.__self__) ##43.12property
propertyデータ記述子です。```python id="o83sqw"
class User:
def init(self, name):
self._name = name
@property
def name(self):
return self._name
アクセス:python id="q15uwm"
u = User("Ada")
print(u.name)
大まかなモデル:```python id="e1wqf0"
class property:
def __get__(self, obj, objtype=None):
if obj is None:
return self
return self.fget(obj)
def __set__(self, obj, value):
if self.fset is None:
raise AttributeError("can't set attribute")
self.fset(obj, value)
```なぜなら`property`定義する`__set__`、それはデータ記述子です。インスタンス辞書よりも優れています。
## 43.13 読み取り専用プロパティ
読み取り専用プロパティは依然としてデータ記述子のように機能します。```python id="bjmyqo"
class Example:
@property
def value(self):
return 42
e = Example()
e.__dict__["value"] = 100
print(e.value)
```出力:```text id="8qw90w"
42
```プロパティにセッターがない場合でも、`property`タイプには`__set__`エラーを引き起こす動作。これがデータ記述子になります。
## 43.14 書き込み可能なプロパティ
書き込み可能なプロパティはセッターを追加します。```python id="7362gp"
class User:
def __init__(self):
self._name = ""
@property
def name(self):
return self._name
@name.setter
def name(self, value):
if not value:
raise ValueError("empty name")
self._name = value
```使用法:```python id="ly2z3r"
u = User()
u.name = "Ada"
print(u.name)
```割り当て:```python id="pzyvzj"
u.name = "Ada"
```記述子の呼び出し`__set__`。
ただ書くだけではありません`u.__dict__["name"]`。
##43.15`staticmethod`
`staticmethod`メソッドバインディングを抑制する記述子です。```python id="7h9gwf"
class Math:
@staticmethod
def add(a, b):
return a + b
print(Math.add(2, 3))
print(Math().add(2, 3))
```どちらの場合も、最初の引数としてインスタンスは挿入されません。
大まかなモデル:```python id="k4n2sv"
class staticmethod:
def __init__(self, func):
self.func = func
def __get__(self, obj, objtype=None):
return self.func
staticmethod関数を保存し、変更せずに返します。
##43.16classmethod
classmethodインスタンスの代わりにクラスをバインドする記述子です。```python id="m0y4q8"
class User:
@classmethod
def create(cls):
return cls()
u = User.create()
大まかなモデル:python id="vn2wvc"
class classmethod:
def init(self, func):
self.func = func
def __get__(self, obj, objtype=None):
if objtype is None:
objtype = type(obj)
return BoundMethod(self.func, objtype)
電話をかける:python id="cn0iaw"
User.create()
は次とほぼ同等です:python id="mzdvxl"
User.dict["create"].get(None, User)()
これは以下を呼び出します:python id="mgiurn"
original_function(User)
```##43.17__slots__および記述子__slots__記述子を使用して、固定レイアウトのインスタンス属性を管理します。
例:```python id="tv0e9k" class Point: slots = ("x", "y")
def __init__(self, x, y):
self.x = x
self.y = y
クラス ディクショナリにはスロット記述子が含まれています。python id="ziiwfx"
print(Point.dict["x"])
print(Point.dict["y"])
スロットがある場合、通常のインスタンスにはスロットがない場合があります。`__dict__`明示的に要求された場合を除きます。```python id="mv07bu"
p = Point(1, 2)
print(hasattr(p, "__dict__"))
```出力:```text id="drcrll"
False
```## 43.18 メンバー記述子とGetset記述子
CPython は、一部の C レベル記述子をオブジェクトとして公開します。
一般的な例:```text id="vtr7cq"
member_descriptor
getset_descriptor
wrapper_descriptor
method_descriptor
```それらはクラス辞書で確認できます。```python id="7ln1br"
class Point:
__slots__ = ("x",)
print(type(Point.__dict__["x"]))
```組み込み型の場合:```python id="inwepf"
print(type(int.__dict__["real"]))
print(type(str.__dict__["upper"]))
print(type(object.__dict__["__str__"]))
```これらは、C レベルの動作をラップする CPython レベルの記述子オブジェクトです。
## 43.19 記述子と属性の割り当て
割り当ての場合:```python id="4ja4xk"
obj.name = value
```CPython は常に書き込みを行うわけではありません`obj.__dict__`。
型にデータ記述子がある場合、`name`、代入により記述子の呼び出しが行われます。`__set__`。
例:```python id="2t9ndx"
class Descriptor:
def __set__(self, obj, value):
print("set", value)
class Example:
x = Descriptor()
e = Example()
e.x = 10
```出力:```text id="jfd4bi"
set 10
```通常のインスタンス ディクショナリへの書き込みは必要ありません。
## 43.20 記述子と属性の削除
削除の場合:```python id="mxpuzk"
del obj.name
```データ記述子が次のように定義している場合、`__delete__`, CPython はそれを呼び出します。```python id="wuc0pw"
class Descriptor:
def __delete__(self, obj):
print("delete")
class Example:
x = Descriptor()
e = Example()
del e.x
```出力:```text id="fkv43g"
delete
```そうしないと、削除によってインスタンス ディクショナリからエントリが削除されたり、エラーが発生したりする可能性があります。`AttributeError`。
##43.21`__set_name__`記述子で定義できるのは、`__set_name__`。```python id="yvbjoc"
class Field:
def __set_name__(self, owner, name):
self.owner = owner
self.name = name
class User:
id = Field()
name = Field()
```クラスの作成中に、Python は以下を呼び出します。```text id="zf52ho"
User.__dict__["id"].__set_name__(User, "id")
User.__dict__["name"].__set_name__(User, "name")
```これにより、記述子は割り当てられた属性名を学習できるようになります。
それなし`__set_name__`、記述子には冗長構成が必要になることがよくあります。```python id="k9fsdd"
id = Field("id")
name = Field("name")
```## 43.22 実用的な検証記述子```python id="okxfod"
class PositiveInt:
def __set_name__(self, owner, name):
self.name = "_" + name
def __get__(self, obj, objtype=None):
if obj is None:
return self
return getattr(obj, self.name)
def __set__(self, obj, value):
if not isinstance(value, int):
raise TypeError("expected int")
if value <= 0:
raise ValueError("expected positive integer")
setattr(obj, self.name, value)
```使用法:```python id="u85219"
class User:
age = PositiveInt()
def __init__(self, age):
self.age = age
u = User(30)
print(u.age)
```課題:```python id="7h7peb"
self.age = age
```電話`PositiveInt.__set__`。
アクセス:```python id="jvsgys"
u.age
```電話`PositiveInt.__get__`。
## 43.23 記述子のストレージの選択
記述子は通常、インスタンスごとの値を別の場所に保存します。
一般的な選択肢:
|ストレージ |例 |トレードオフ |
|---|---|---|
|インスタンス辞書 |`obj.__dict__[name]`|シンプル、必要なもの`__dict__`|
|プライベート属性 |`obj._name`|シンプル、衝突可能 |
|弱いキー辞書 |`WeakKeyDictionary[obj]`|インスタンスの辞書に影響を与えずに動作しますが、オーバーヘッドが高くなります。
|スロット オフセット | CPython 内部 |高速な固定レイアウト |
|外部ストア | ORM/セッション/状態テーブル |フレームワークに役立つ |
記述子オブジェクトは通常、クラスによって共有されるため、インスタンス固有のデータを記述子に直接保存することは通常間違っています。
悪い:```python id="httjyo"
class BadField:
def __set__(self, obj, value):
self.value = value
```すべてのインスタンスは 1 つの記述子オブジェクトを共有します。
より良い:```python id="qgjw4l"
class Field:
def __set_name__(self, owner, name):
self.name = "_" + name
def __set__(self, obj, value):
setattr(obj, self.name, value)
```## 43.24 記述子の共有
記述子はクラスに保存されます。```python id="poo2z7"
class Field:
pass
class User:
name = Field()
```1つあります`Field`の対象`User.name`。
全て`User`インスタンスは同じ記述子オブジェクトにアクセスします。```python id="ghzk97"
print(User.__dict__["name"])
```このため、記述子の状態は慎重に設計する必要があります。
記述子レベルの状態は次の場合に適しています。```text id="lzdzy9"
field name
validation rule
default configuration
metadata
owner class
```インスタンスレベルの状態は、インスタンスまたは外部のインスタンスごとのストアに属します。
## 43.25 記述子と継承
記述子は他のクラス属性と同様に継承されます。```python id="07zg83"
class Field:
def __get__(self, obj, objtype=None):
return "value"
class Base:
x = Field()
class Child(Base):
pass
print(Child().x)
```出力:```text id="23j8kz"
value
```ルックアップでは、メソッドの解決順序を検索します。記述子が基本クラスで見つかった場合、その記述子は子インスタンスの属性アクセスに参加します。
サブクラスは、同じ名前を定義することで記述子をオーバーライドできます。```python id="jgv5bi"
class Child(Base):
x = 100
```今:```python id="a343w1"
print(Child().x)
```戻り値:```text id="sbrd9y"
100
```他の検索ルールが適用されない限り。
## 43.26 記述子と`super`
`super()`記述子バインディングも使用します。
例:```python id="klco94"
class Base:
def method(self):
return "base"
class Child(Base):
def method(self):
return super().method()
```式:```python id="vz6j6v"
super().method
```見つけます`method`MRO 内の次のクラスでそれを元のインスタンスにバインドします。
このバインディングでは依然として記述子ロジックが使用されています。
## 43.27 記述子と`__getattribute__`通常の属性アクセスはすべて通過します`__getattribute__`。```python id="0h0zfk"
obj.name
```大まかに次のように呼びます。```python id="wrm9c8"
type(obj).__getattribute__(obj, "name")
```デフォルトの実装では、`object.__getattribute__`、記述子の検索を実装します。
クラスがオーバーライドする場合`__getattribute__`、記述子の動作を変更またはバイパスすることができます。
例:```python id="6hlojz"
class Example:
@property
def x(self):
return 42
def __getattribute__(self, name):
return "intercepted"
e = Example()
print(e.x)
```出力:```text id="i8f18x"
intercepted
```プロパティに到達することはありません。`__getattribute__`すべてを傍受します。
## 43.28 記述子と`__getattr__`
`__getattr__`は、通常の検索が失敗した後にのみ呼び出されます。```python id="1tg4g8"
class Example:
def __getattr__(self, name):
return "missing"
e = Example()
print(e.anything)
```出力:```text id="cafj83"
missing
```記述子が存在し、値を返す場合、`__getattr__`は呼ばれません。
検索順序:```text id="hpfu2m"
__getattribute__ runs first
descriptor rules happen inside normal __getattribute__
__getattr__ handles missing names only
```## 43.29 記述子とメタクラス
クラス属性へのアクセスでも記述子ロジックが使用されますが、検索されるオブジェクトはクラス オブジェクトです。```python id="6g2hvo"
class Meta(type):
@property
def label(cls):
return cls.__name__.lower()
class User(metaclass=Meta):
pass
print(User.label)
```ここ、`label`メタクラスの記述子です。アクセスする`User.label`記述子の検索を呼び出します`type(User)`。
これが、メタクラスが計算されたクラス属性を定義できる理由です。
## 43.30 クラスの記述子検索
対象:```python id="bugwo3"
Class.attr
```ルックアップはによって処理されます`type.__getattribute__`。
検索はクラス オブジェクトとそのメタクラスに対して行われます。
メタクラス内の属性が記述子の場合、オブジェクトとしてクラスにバインドできます。
このようにして`classmethod`、メタクラス プロパティ、および多くの組み込み型操作が機能します。
クラス検索とインスタンス検索は関連していますが、同一ではありません。インスタンス ルックアップでは、インスタンス タイプの MRO を検索します。クラス検索はメタクラス機構を通じて検索します。
## 43.31 ORM の記述子
記述子は ORM で一般的です。
形状の例:```python id="gs116j"
class Column:
def __set_name__(self, owner, name):
self.name = name
def __get__(self, obj, objtype=None):
if obj is None:
return self
return obj._row[self.name]
def __set__(self, obj, value):
obj._row[self.name] = value
```使用法:```python id="3gj06d"
class User:
id = Column()
name = Column()
def __init__(self, row):
self._row = row
```それから:```python id="e6nz6o"
u = User({"id": 1, "name": "Ada"})
print(u.name)
u.name = "Grace"
```記述子は、属性アクセスを行ストレージにマップします。
## 43.32 検証ライブラリの記述子
検証フレームワークは記述子を使用して制約を強制します。```python id="kcyh88"
class String:
def __set_name__(self, owner, name):
self.storage = "_" + name
def __set__(self, obj, value):
if not isinstance(value, str):
raise TypeError("expected str")
setattr(obj, self.storage, value)
def __get__(self, obj, objtype=None):
if obj is None:
return self
return getattr(obj, self.storage)
```使用法:```python id="4vl57c"
class User:
name = String()
def __init__(self, name):
self.name = name
```クラス定義ではフィールドを宣言します。記述子はルールを強制します。
## 43.33 キャッシュされた計算における記述子
キャッシュされたプロパティは非データ記述子です。```python id="yh220g"
class cached_property:
def __init__(self, func):
self.func = func
self.name = func.__name__
def __get__(self, obj, objtype=None):
if obj is None:
return self
value = self.func(obj)
obj.__dict__[self.name] = value
return value
```使用法:```python id="kxogrn"
class Data:
@cached_property
def total(self):
print("computing")
return 100
d = Data()
print(d.total)
print(d.total)
```最初のアクセスでは、記述子を呼び出し、値をインスタンス ディクショナリに保存します。
2 回目のアクセスでは、記述子がデータではないため、インスタンス ディクショナリ値が返されます。
これは記述子の優先順位に直接依存します。
## 43.34 データと非データの設計ルール
データ記述子は、記述子が書き込みを制御する必要がある場合、またはインスタンス属性によってオーバーライドされてはならない場合に使用します。
インスタンスレベルのキャッシュまたはシャドウイングが必要な場合は、非データ記述子を使用します。
|記述子の種類 |定義 |インスタンス辞書はオーバーライドできますか? |一般的な例 |
|---|---|---:|---|
|非データ記述子 |`__get__`のみ |はい |関数、キャッシュされたプロパティ |
|データ記述子 |`__set__`または`__delete__`|いいえ |プロパティ、スロット、フィールドの検証 |
この 1 つの違いにより、多くの属性アクセス動作が説明されます。
## 43.35 記述子用の CPython 内部スロット
C レベルでは、記述子の動作は型スロットを通じて表されます。
関連する操作は以下に対応します。```text id="3v2e6f"
tp_descr_get
tp_descr_set
```を実装するタイプ`tp_descr_get`まるでそうであるかのように振る舞うことができる`__get__`。
を実装するタイプ`tp_descr_set`まるでそうであるかのように振る舞うことができる`__set__`または`__delete__`。
組み込み記述子は多くの場合、その型がこれらのスロットを提供する C オブジェクトです。
これは、組み込みメソッド、スロット記述子、getset 記述子、およびラッパー記述子が通常の Python ルックアップと統合される方法です。
## 43.36 CPython 用語での属性検索
簡略化された CPython レベルのルックアップ`obj.name`:
```text id="m04p6t"
1. type = Py_TYPE(obj)
2. descr = lookup name in type MRO
3. if descr has tp_descr_get and tp_descr_set:
return descr.__get__(obj, type)
4. if obj has dict and name in dict:
return dict[name]
5. if descr has tp_descr_get:
return descr.__get__(obj, type)
6. if descr exists:
return descr
7. call fallback or raise AttributeError
```これは記述子の動作の中心です。
## 43.37 記述子のエラー
記述子は上げる必要があります`AttributeError`通常の属性フォールバック動作が必要な場合に、欠落している属性に対して使用します。
例:```python id="xeje8w"
class Maybe:
def __get__(self, obj, objtype=None):
raise AttributeError("not available")
```を呼び出す記述子`AttributeError`と相互作用する可能性があります`getattr`、`hasattr`、およびフォールバックメカニズム。
正確な例外を発生させます。無関係なエラーを非表示にしないでください。`AttributeError`属性が本当に利用できない場合を除きます。
## 43.38 記述子のイントロスペクション
記述子を検査するには、クラス ディクショナリを通じてアクセスします。```python id="047r6b"
class Example:
@property
def value(self):
return 42
print(Example.__dict__["value"])
```クラスを介してアクセスすると、`__get__`:
```python id="2rtf24"
print(Example.value)
```のために`property`、クラスアクセスはプロパティオブジェクトを返します。ただし、他の記述子は計算値を返す場合があります。
通常、生の記述子を取得する最も安全な方法は次のとおりです。```python id="gvvuc9"
vars(Example)["value"]
```または:```python id="h6m1qm"
Example.__dict__["value"]
```## 43.39 一般的な記述子のバグ
|バグ |原因 |修正 |
|---|---|---|
|すべてのインスタンスは 1 つの値を共有します |記述子に保存されたインスタンスの状態 |状態をインスタンスまたは外部のインスタンスごとのマップに保存します。
|カスタム クラスによって無視されるプロパティ |オーバーライド`__getattribute__`間違って |に委任する`object.__getattribute__`|
|キャッシュされたプロパティはキャッシュされません |記述子はデータ記述子です |非データにするか、カスタムの優先順位ロジックを作成します。
|インスタンス属性はフィールドをオーバーライドできません |記述子の定義`__set__`|オーバーライドが必要な場合は、非データ記述子を使用します。
|記述子にフィールド名がありません |実装しなかった`__set_name__`|追加`__set_name__`または明示的に名前を渡します |
|クラスアクセスの中断 |`__get__`扱いません`obj is None`|記述子またはクラスレベルのオブジェクトを返す |
## 43.40 重要なポイント
記述子は定義するオブジェクトです。`__get__`、`__set__`、 または`__delete__`。
非データ記述子は以下を定義します`__get__`のみ。
データ記述子は以下を定義します`__set__`または`__delete__`。
データ記述子はインスタンス ディクショナリより優先されます。
非データ記述子は、インスタンス ディクショナリによってシャドウされる可能性があります。
関数は記述子であるため、メソッドはインスタンスにバインドされます。`property`、`staticmethod`、`classmethod`、スロット、組み込みメソッド、および多くの CPython 内部では記述子が使用されます。
記述子は、型オブジェクトの記述子スロットを通じて CPython に実装されます。
メソッド、プロパティ、スロット、メタクラス、属性検索を理解するには、記述子を理解する必要があります。