在 Python 中禁用描述符对象

Disable descriptor object in Python

这是 descriptor 在 Python 中用法的简单示例:

class MyDescriptor(object):
  def __get__(self, obj, objtype):
    return 42

class MyClass(object):
  d = MyDescriptor()

print MyClass().d  # 42

但是如果我希望描述符对象成为 class 的 actual 属性怎么办,那么 MyClass().d 将是 MyDescriptor 实例, 不是 42.

有很多方法可以得到你想要的,这取决于你到底想要什么。

如果您只想访问描述符对象(即,您不关心它是否实际上作为属性直接存储在 class 上),那么只需将其存储在列表或其他内容中:

class Foo(object):
    descriptor = [MyDescriptor()]

这将需要额外的间接级别来访问它,但在实践中,这可能比替代方案更简单。

the documentation 中所述,描述符是通过 object.__getattribute__ 激活的。所以如果你真的想让它成为一个属性,你可以在包含描述符(MyClass)的class上覆盖__getattribute__,从而阻止描述符机制生效:

class MyClass(object):
  d = MyDescriptor()

  def __getattribute__(self, attr):
    if attr=='d':
        return self.__class__.__dict__['d']
    else:
        return super(MyClass, self).__getattribute__(attr)

>>> MyClass().d
<__main__.MyDescriptor object at 0x0000000002843E80>

使用inspect.getattr_static

getattr_static() does not resolve descriptors, for example slot descriptors or getset descriptors on objects implemented in C. The descriptor object is returned instead of the underlying attribute.

>>> import inspect
>>> inspect.getattr_static(MyClass, 'd')
<__main__.MyDescriptor object at 0x105678c18>
>>> inspect.getattr_static(MyClass(),"d")
<__main__.MyDescriptor object at 0x105678c18>

如果您想将此设置为实例的默认行为,您可以将 __getattribute__ 重写为 getattr_static:

class MyClass(object):
  from inspect import getattr_static as __getattribute__
  d = MyDescriptor()

>>> MyClass().d
<__main__.MyDescriptor object at 0x105678c18>

坦率地说,我不明白你为什么想要这个,将描述符存储在不调用描述符的数据结构中更有意义 dict:

descriptors = {"d": MyDescriptor()}

然后您可以查找 descriptors["d"] 而无需调用描述符的风险。

我最简单的想法是将这个描述符包装到另一个描述符中。它会起作用,因为只会调用一个 __get__

class TrivialDescriptor(object):
  def __init__(self, data):
    self._data = data

  def __get__(self, obj, objtype):
    return self._data

class MyDescriptor(object):
  def __get__(self, obj, objtype):
    return 42

class MyClass(object):
  d = TrivialDescriptor(MyDescriptor())