【问题标题】:Function conflict between __get__ and __getattribute____get__ 和 __getattribute__ 之间的函数冲突
【发布时间】:2021-12-25 11:05:19
【问题描述】:

我正在用 python 3.9 编写一些代码,最近刚刚发现了描述符。

我创建了一个如下所示的类:

import threading

class OuterClass():
    class DecoratorTest():
        def __init__(self,fget,fset=None) -> None:
            self.fget=fget
            self.fset=fset
            self.event=threading.Event()
        def __getattribute__(self, name: str):
            return object.__getattribute__(self,name)
        def __get__(self,obj,objtype=None):
            return self.fget(obj)
        def __set__(self,obj,value):
            self.fset(obj,value)
            self.event.set()
            self.event.clear()
         def setter(self,fset):
            return type(self)(self.fget,fset)
      
    def __init__(self):
        self.random_value=10
    @DecoratorTest
    def test_property(self):
        return self.random_value*2
    @test_property.setter
    def test_property(self,value):
        self.random_value=value

我的意图是有一个装饰器,它将一个事件添加到被装饰的属性中,以便在更改属性时设置事件。这是我如何使用它的示例:

class_iteration=OuterClass()

def test_funct():
    while True:
        class_iteration.test_property.event.wait()
        print("Changed!")

threading.Thread(target=test_funct).start()

class_iteration.test_property=100

问题是当我尝试获取事件时,它认为class_iteration.test_property 是一个 int 对象,因此会引发错误。我想知道在使用__getattribute__ 时是否有任何方法可以绕过__get__,以便我可以访问event 变量,或者我是否需要完全改变我的方法。

【问题讨论】:

  • 你需要吸气剂吗?或者可以通过例如访问它吗? instance.test_property.get?
  • 您可以从__dict__ 的类中检索属性:OuterClass.__dict__['test_property'].event。警告:在您的示例中,Event 将在所有OuterClass 实例之间共享,因为DecoratorTest.__init__ 在定义OuterClass 时被调用,而不是在实例化时调用。这就是为什么test_propertyOuterClass.__dict__ 中,而不是在class_iteration.__dict__ 中的原因。您可以将调试print() 添加到DecoratorTest.__init__ 以确保(应该打印两次,因为setter 创建了然后实例化的新类)。

标签: python get descriptor getattribute


【解决方案1】:

问题是由于两件事。其中之一与描述符类的实现中的__get__() 方法的实现有关。它不会检查其 obj 参数是否为 None,就像文档中描述的 pure Python Property class 中显示的那样。

这很重要,因为它将允许您解决第二个问题,即test_funct() 线程如何访问test_property — 它需要通过类而不是实例来访问。
所以,而不是:

class_iteration.test_property.event.wait()

它应该使用:

type(class_iteration).test_property.event.wait()

(请注意,在下面的代码中,我已将此变量的名称从 class_iteration 更改为 class_instance,以更好地反映它的实际含义。)

这是因为threading.Event 实例由OuterClass 的所有实例共享,因为DecoratorTest 类实例是在其类定义执行时创建的。

下面是带有这些修复的代码(以及我为稍微扩展测试并防止它在结束时挂起的一些其他代码)。我还对其进行了重新格式化以遵守PEP 8 - Style Guide for Python Code 准则以提高可读性。

import threading
import time


class OuterClass:
    class DecoratorTest:
        def __init__(self, fget, fset=None) -> None:
            self.fget = fget
            self.fset = fset
            self.event = threading.Event()

        def __getattribute__(self, name: str):
            return object.__getattribute__(self,name)

        def __get__(self, obj, objtype=None):
            if obj is None:  # Called for decorator instance?
                return self
            if self.fget is None:
                raise AttributeError(f'unreadable attribute {self._name}')
            return self.fget(obj)

        def __set__(self, obj, value):
            self.fset(obj, value)
            self.event.set()
            self.event.clear()

        def setter(self, fset):
            return type(self)(self.fget, fset)

    def __init__(self):
        self.random_value = 10

    @DecoratorTest
    def test_property(self):
        return self.random_value*2

    @test_property.setter
    def test_property(self,value):
        self.random_value = value


class_instance = OuterClass()

def test_funct():
    while True:
        type(class_instance).test_property.event.wait()
        print(f"Change detected: {class_instance.test_property=}")

# Daemon thread so won't prevent termination of main thread if it's running.
threading.Thread(target=test_funct, daemon=True).start()

print(f"Initial value: {class_instance.test_property=}")
time.sleep(.1)
class_instance.test_property = 100
time.sleep(.1)
class_instance.test_property = 200
time.sleep(.1)

【讨论】:

  • 这不能回答你的问题吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-11-06
  • 2016-07-05
  • 1970-01-01
  • 1970-01-01
  • 2012-06-27
  • 2012-03-06
  • 2011-05-02
相关资源
最近更新 更多