使用 __set__ 获得 class 级别类型描述符的方法

Way to have a class level type descriptor with __set__

我有数据描述符适用于具有 __set____get__ 的对象。

然而,class 描述符似乎不支持 __set__。这样做会用分配的值替换描述符对象本身。

以下代码对此进行了演示

from __future__ import print_function

class Descriptor(object):
    def __get__(self, obj, cls):
        print('__get__')
    def __set__(self, obj, value):
        print('__set__')

class Class(object):
    descriptor = Descriptor()

print('Object')
a = Class()
a.descriptor
a.descriptor = 1

print('Class')
Class.descriptor
Class.descriptor = 2

输出

Object
__get__
__set__
Class
__get__

如您所见,class 级别 __set__ 没有被调用。

是否有一些解决方法或技巧(无论多么可怕)允许 class 上的 __set__ 数据描述符?

明确一点,我不希望调用代码必须实现任何 'hack'。我希望调用代码按上面的预期工作,但任何 hack 都是 'behind the scenes'.

使用 Python 2.7

我不会进入整个 Descriptor 协议。我自己并不完全理解;事实上,您已经提醒我,我需要戒掉懒惰,真正 投入其中。同时,我会这样说:

的理解是描述符只会在实例上发挥它们的魔力。现在,您可能已经知道这一点,这就是为什么您想知道是否有破解此限制的原因。

如果您稍微熟悉元classes,您就会知道classes 也是实例。 类 可以是 class 的实例,也可以是实例等等。这很好,因为你要问的看起来像这样:

class Descriptor(object):
    def __get__(self, obj, cls):
        print('__get__')
    def __set__(self, obj, value):
        print('__set__')

class MetaClass(type):
    descriptor = Descriptor()

class Class(object):
    __metaclass__ = MetaClass

# This will work fine when you do Class.descriptor, as you asked
# but it will raise an AttributeError if you do 
# a = Class()
# a.descriptor
# Read on for the full explanation...

MetaClass中定义的descriptor变量仅对Classclass可见。任何尝试调用它的 Class 实例都会给你一个 AttributeError。这是因为 classes 的实例,当搜索一个属性时,在搜索 class 的 __dict__ 之前先搜索他们自己的 __dict__,但不会搜索那么远作为 class 的 __metaclass__。现在,如果你想同时使用它并为 class 及其实例使用相同的变量名(尽管我不推荐它,因为它会引起混淆),你可以这样做:

class Descriptor(object):
    def __get__(self, obj, cls):
        print('__get__')
    def __set__(self, obj, value):
        print('__set__')

class MetaClass(type):
    descriptor = Descriptor()

class Class(object):
    __metaclass__ = MetaClass
    descriptor = Descriptor()

此时您可能想知道:如果一个实例在搜索它的 class 之前先搜索它自己的 __dict__,那怎么会调用“Class.descriptor”如果 Class.descriptor 本质上是它自己的实例变量(来自 metaclass视角)

答案是数据描述符(同时定义了 __get____set__ 的描述符)与非数据描述符(只定义了 __get__)不同优先于实例变量。换句话说,MetaClass 中的 descriptor 变量是 Class 将选择的变量,因为它优先于 Class 自己的 descriptor 变量。 Class 实例也是如此,它会自动获取 Class.

中定义的 descriptor 变量

希望我没有让您感到困惑。这些东西很容易忘记,我认为更是如此,因为大多数时候了解这种魔法水平不是很常见也没有必要。我不得不刷新我对这个的记忆!好问题:)