我有一个 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))
让我们一步一步地看一下:
当
Outer
类定义时,会在其命名空间中创建一个名为Inner
的描述符,该描述符又具有内部
类(在外部范围中定义)作为self.cls
参数。当执行
foo = Outer()
时,__init__
被调用。因为Inner
现在指向一个描述符,self.Inner
触发__get__
(因为描述符是从实例调用的这是self
)。从
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/