【问题标题】:How to use __get__ and __set__ (python descriptors)如何使用 __get__ 和 __set__(python 描述符)
【发布时间】:2021-04-25 11:28:26
【问题描述】:

我是使用描述符的新手,我想我对它们的工作原理有很好的理解,但我遇到了一个问题,我不知道如何解决它。

代码

class Foo:
    class Bar:
        def __get__(self,instance, owner):
            return 10
        def __set__(self,instance,value):
            raise Exception
    bar=Bar()

print(Foo.bar)
Foo.bar=5
print(Foo.bar)

输出

>>> 10
>>> 5

我试图将bar 设为常量以进行测试,我知道property 装饰器,但我更喜欢使用描述符。

首先我打印出bar 的值以查看__get__ 是否有效 - 确实有效,输出为10。

但是,当我将5 分配给bar 时,预期的结果将是一个例外,但发生的情况是5 被分配给bar,尽管指定了__set__,所以当我再次打印时,第二个输出是5.

谁能告诉我为什么没有提出错误?

【问题讨论】:

    标签: python python-3.x oop descriptor


    【解决方案1】:

    来自the docs:

    object.__set__(self, instance, value)

    调用以将所有者类的实例instance 上的属性设置为新值value。

    在您的代码中,Foo.bar = 5 正在设置类属性,而不是实例属性。如果你确实使用了一个实例(没有首先设置Foo.bar = 5,它会覆盖你的描述符),那么你会按预期得到一个异常:

    >>> f = Foo()
    >>> f.bar
    10
    >>> f.bar = 5
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 6, in __set__
    Exception
    

    如果您希望在设置类属性时应用__set__ 行为,那么类本身需要是使用描述符的metaclass 的实例:

    class FooMeta(type):
        class Bar:
            def __get__(self,instance, owner):
                return 10
            def __set__(self,instance,value):
                raise Exception
        bar = Bar()
    
    class Foo(metaclass=FooMeta):
        pass
    

    测试:

    >>> Foo.bar
    10
    >>> Foo.bar = 5
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 6, in __set__
    Exception
    

    【讨论】:

    • 谢谢,正是我所需要的,但有没有办法只使用一个类?那么FooMeta和Foo的组合呢?
    猜你喜欢
    • 1970-01-01
    • 2011-04-17
    • 1970-01-01
    • 2021-12-19
    • 1970-01-01
    • 2016-07-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多