python - 描述符的 __set__ 未调用

标签 python python-decorators inner-classes python-descriptors

我有一个 innerclass 装饰器/描述符,它应该将外部实例作为第一个参数传递给内部可调用对象:

from functools import partial

class innerclass:

  def __init__(self, cls):
    self.cls = cls

  def __get__(self, obj, obj_type=None):
    if obj is None:
      return self.cls

    return partial(self.cls, obj)

这是一个名为 Outer 的类,其 .Inner 是用 innerclass 修饰的类:

class Outer:

  def __init__(self):
    self.inner_value = self.Inner('foo')

  @innerclass
  class Inner:

    def __init__(self, outer_instance, value):
      self.outer = outer_instance
      self.value = value

    def __set__(self, outer_instance, value):
      print('Setter invoked')
      self.value = value

我预计当我更改属性时会调用 setter 。然而,事实并非如此:

foo = Outer()
print(type(foo.inner_value))  # <class '__main__.Outer.Inner'>

foo.inner_value = 42
print(type(foo.inner_value))  # <class 'int'>

这是为什么?我该如何解决?

最佳答案

如果你改变:

self.inner_value = self.Inner('foo')

至:

Outer.inner_value = self.Inner('foo')

您将看到“已调用 Setter”


这是对您的代码的重写,可能有助于解释发生的情况:

from functools import partial


class Descriptor:
    def __init__(self, cls):
        self.cls = cls

    def __get__(self, obj, obj_type=None):
        if obj is None:
            return self.cls

        return partial(self.cls, obj)


class Inner:
    def __init__(self, outer_instance, value):
        self.outer = outer_instance
        self.value = value

    def __set__(self, outer_instance, value):
        print("Setter invoked")
        self.value = value


class Outer:
    def __init__(self):
        self.inner_value = self.Inner("foo")

    Inner = Descriptor(Inner)


foo = Outer()
print(type(foo.inner_value))

foo.inner_value = 42
print(type(foo.inner_value))

让我们一步一步地看一下:

  1. Outer定义时,会在其命名空间中创建一个名为 Inner 的描述符,该描述符又具有 内部类(在外部范围中定义)作为self.cls参数。

  2. 当执行foo = Outer()时,__init__被调用。因为 Inner 现在指向一个描述符,self.Inner 触发 __get__(因为描述符是从实例调用的这是self)。

  3. self.Inner("foo") 返回什么?在全局范围内定义的 Inner 类的实例。它也是一个带有__set__的描述符。所以 self.inner_value 指向这个描述符对象。

但问题是这个描述符位于 foo 的命名空间内。不是类Outer!:

>>> "inner_value" in foo.__dict__
True

因此,通过执行foo.inner_value = 42,您不会触发__set__。您只需将其覆盖为 42:

>>> foo = Outer()
>>> foo.__dict__
{'inner_value': <__main__.Inner object at 0x7fd716f8c910>}
>>> foo.inner_value = 42
>>> foo.__dict__
{'inner_value': 42}
>>> 

如果要查看描述符行为,self.Inner("foo") 的返回值必须位于类中:

class Outer:
    def __init__(self):
        Outer.inner_value = self.Inner("foo")  # <--

    Inner = Descriptor(Inner)

现在它将输出:

<class '__main__.Inner'>
Setter invoked
<class '__main__.Inner'>

关于python - 描述符的 __set__ 未调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76292635/

相关文章:

python-3.x - 如何使用运算符来组合函数?

python - 如何输入提示二阶装饰器/装饰器工厂 (PEP-612 ParamSpec)

java - 通过子类包限定符导入父类的内部接口(interface)

c++ - 使内部类成为 C++ 中的 friend

c# - 为什么我需要使用 C# 嵌套类

Python xpath : try an xpath, 除了填写给定的值

python - 使用 Python 在 Power BI 中筛选 Python Script Visual 中的数据

Django - View accounts.decorators.wrapper_function 没有返回 HttpResponse 对象。它返回 None 而不是

python - 从循环中保存多个数据帧

python - 如何使用存储在 Google Cloud Storage 中的图像调用 Google Vision API?