2

この単純なクラスは、基本的な python オブジェクトの属性アクセスを模倣しようとします。dict属性とクラスをcls明示的に格納します。その結果.x、インスタンスにアクセスすると が返されるdict[x]か、失敗した場合は が返されますcls.x。通常のオブジェクトと同じように。

class Instance(object):
    __slots__ = ["dict", "cls"]
    def __getattribute__(self, key):
        try:
            return self.dict[key]
        except KeyError:
            return getattr(self.cls, key)
    def __setattr__(self, key, value):
        if key == "__class__":
            self.cls = value
        else:
            self.dict[key] = value

しかし、それはそれほど単純ではありません。明らかな問題の 1 つは、記述子が完全に無視されていることです。clsプロパティがあると想像してみてください。で定義Instance.some_property = 10されているようにプロパティにアクセスする必要がありますが、cls代わりにsome_propertyで属性として喜んで設定されますdict

cls次に、 のメソッドを のインスタンスにバインドするという問題がありInstance、おそらくそれ以上の問題があります。

上記のクラスをできる限り Python オブジェクトに近づけて機能させるには、多くの詳細があるようです。これまで読んだ記述子のドキュメントでは、簡単に言えば、すべてを正しくする方法が明確にされていません。

私が求めているのは、Python の属性アクセスの完全な代替を実装するためのリファレンスです。つまり、上記のクラスですが、正しいです。

4

1 に答える 1

1

さて、私はこの答えが必要だったので、調査をしなければなりませんでした。以下のコードは、以下をカバーしています。

  • データ記述子は、属性の設定と取得の両方で優先されます。
  • 非データ記述子が適切に呼び出される__getattribute__

内部プロジェクトから翻訳する必要があったため、以下のコードにはタイプミスがある可能性があります。そして、それが 100% python オブジェクトのようであるかどうかはわかりません。そのため、誰かがエラーを見つけることができれば、それは素晴らしいことです。

_sentinel = object()

def find_classattr(cls, key):
  for base in cls.__mro__: # Using __mro__ for speed.
    try: return base.__dict__[key]
    except KeyError: pass
  return _sentinel

class Instance(object):
  __slots__ = ["dict", "cls"]
  def __init__(self, d, cls):
    object.__setattr__(self, "dict", d)
    object.__setattr__(self, "cls", cls)
  def __getattribute__(self, key):
    d = object.__getattribute__(self, "dict")
    cls = object.__getattribute__(self, "cls")
    if key == "__class__":
      return cls
    # Data descriptors in the class, defined by presence of '__set__',
    # overrides any other kind of attribute access.
    cls_attr = find_classattr(cls, key)
    if hasattr(cls_attr, '__set__'):
      return cls_attr.__get__(self, cls)
    # Next in order of precedence are instance attributes.
    try:
      return d[key]
    except KeyError:
      # Finally class attributes, that may or may not be non-data descriptors.
      if hasattr(cls_attr, "__get__"):
        return cls_attr.__get__(self, cls)
      if cls_attr is not _sentinel:
        return cls_attr

    raise AttributeError("'{}' object has no attribute '{}'".format(
      getattr(cls, '__name__', "?"), key))
  def __setattr__(self, key, value):
    d = object.__getattribute__(self, "dict")
    cls = object.__getattribute__(self, "cls")
    if key == "__class__":
      object.__setattr__(self, "cls", value)
      return

    # Again, data descriptors override instance attributes.
    cls_attr = find_classattr(cls, key)
    if hasattr(cls_attr, '__set__'):
      cls_attr.__set__(self, value)
    else:
      d[key] = value

面白いことに、数年前にまったく同じものを書いたことに気づきましたが、記述子プロトコルは非常に難解で、それ以来忘れていました。

getattr編集:クラスの属性を検索するために使用すると、クラス レベル (つまり、インスタンスなし) でその記述子が呼び出されるバグを修正しました。__dict__塩基の ​​を直接見るメソッドに置き換えました。

于 2012-10-04T11:12:00.610 に答える