[英]How can I pass an instance binding descriptor as a reference argument to a function?
如果没有直接的解决方案,这可能有点棘手,但我也会对更复杂的解决方案感到满意:
我有一个实例绑定数据描述符绑定到一个全局实例,我想将它作为 function 参数传递而不被评估(即__get__()
执行)。 在此代码示例中,它不起作用,并且描述符将int
类型的当前值10
传递给 function 参数,而不是其自身:
"""A 'instance binding data descriptor' that sets and returns values
normally and prints a message logging their access.
The `x`-value is stored in the instance dictionary `my_instance.__dict__`.
"""
def __init__(self, init_value=None):
self.init_value = init_value
def __get__(self, instance, owner):
value = getattr(instance, self.__name__ + '_value')
print('I AM A DATA-DESCRIPTOR retrieving `{}` value: {}'.format(self.__name__, value))
return value
def __set__(self, instance, value):
print('I AM A DESCRIPTOR updating `{}` to value: {}'.format(self.__name__, value))
setattr(instance, self.__name__ + '_value', value)
class MyClass(object):
x = RevealAccess(init_value=10)
def __new__(cls):
instance = object.__new__(cls)
for desc_name in [key for key in cls.__dict__.keys() if isinstance(cls.__dict__[key], RevealAccess)]:
cls.__dict__[desc_name].__name__ = desc_name
instance.__dict__[desc_name + '_value'] = cls.__dict__[desc_name].init_value
return instance
my_instance = MyClass()
def func_with_descriptor_as_argument(descriptor_arg):
print('\n\nINSIDE the function `descriptor_arg=my_instance.x`results in: {}'.format(descriptor_arg))
print('`type(descriptor_arg)`: {}'.format(type(descriptor_arg)))
print('Changing `my_instance.x` value results in:')
descriptor_arg = 5
print('INSIDE the function after changing `my_instance.x` = {}\n\n'.format(descriptor_arg))
if __name__ == '__main__':
print('\n\nOUTSIDE the function `my_instance.x`: {}'.format(my_instance.x))
print('Changing `my_instance.x` value results in:')
my_instance.x = 5
print('OUTSIDE the function after changing `my_instance.x` = {}\n\n'.format(my_instance.x))
print('Reset:')
my_instance.x = 10
func_with_descriptor_as_argument(descriptor_arg=my_instance.x)
output 是:
I AM A DATA-DESCRIPTOR retrieving `x` value: 10
OUTSIDE the function `my_instance.x`: 10
Changing `my_instance.x` value results in:
I AM A DESCRIPTOR updating `x` to value: 5
I AM A DATA-DESCRIPTOR retrieving `x` value: 5
OUTSIDE the function after changing `my_instance.x` = 5
Reset:
I AM A DESCRIPTOR updating `x` to value: 10
I AM A DATA-DESCRIPTOR retrieving `x` value: 10
INSIDE the function `descriptor_arg=my_instance.x`results in: 10
`type(descriptor_arg)`: <class 'int'>
Changing `my_instance.x` value results in:
INSIDE the function after changing `my_instance.x` = 5
我明白它不是这样工作的。 但我想要的是在 function 中操作全局实例字典值my_instance.__dict__['x_value']
。 我必须对许多实例和函数重复这一点,并且实际的描述符也在做其他事情(在这个例子中,它只打印“我是......”但在我的例子中,它是例如类型检查、触发其他进程等),所以不希望直接进行字典操作。 它必须由描述符完成。
我可以构建某种描述符来传递对 function 参数的引用,该参数的行为等效吗?
我一直在寻找不同的选择:
my_instance
和字符串名称x
或作为元组传递,并在 function 中使用getattr()
、 setattr()
。 我不喜欢它,因为它是一个框架,对任何人都不好。inspect
,一些AST
包,然后在重载的__get__()
中构建一个适当的引用并返回它。 我可以管理检测部分,但我不知道参考的外观如何? 将其包装在另一个描述符中? 最后,function 参数应该在 function 中像直接可调用的描述符一样工作,但获取/设置全局字典my_instance.__dict__['x_value']
(并执行提到的所有其他内容)。
我很高兴有任何想法并期待讨论!
我找到了一个很好的解决方案,它是一种类包装器,如果您尝试从中获取不存在的属性,则描述符所有者实例my_instance
在__getattr__()
中创建并返回。
调整上面的例子,解决方案看起来像这样:
class RevealAccess(object):
"""A 'instance binding data descriptor' that sets and returns values
normally and prints a message logging their access.
The `x`-value is stored in the instance dictionary `my_instance.__dict__`.
"""
def __init__(self, init_value=None):
self.init_value = init_value
def __get__(self, instance, owner):
value = getattr(instance, self.__name__ + '_value')
print('I AM A DATA-DESCRIPTOR retrieving `{}` value: {}'.format(self.__name__, value))
return value
def __set__(self, instance, value):
print('I AM A DESCRIPTOR updating `{}` to value: {}'.format(self.__name__, value))
setattr(instance, self.__name__ + '_value', value)
class DescriptorReference:
def __init__(self, instance, descriptor_name):
self.__dict__['instance'] = instance
self.__dict__['descriptor_name'] = descriptor_name
def __getattr__(self, name):
return object.__getattribute__(self.instance, self.descriptor_name)
def __setattr__(self, dummy, value):
setattr(self.instance, self.descriptor_name, value)
class MyClass(object):
x = RevealAccess(init_value=10)
def __new__(cls):
instance = object.__new__(cls)
for desc_name in [key for key in cls.__dict__.keys() if isinstance(cls.__dict__[key], RevealAccess)]:
cls.__dict__[desc_name].__name__ = desc_name
instance.__dict__[desc_name + '_value'] = cls.__dict__[desc_name].init_value
return instance
def __getattr__(self, name):
return DescriptorReference(instance=self, descriptor_name=self.__class__.__dict__['x'].__name__)
my_instance = MyClass()
def func_with_descriptor_value_as_argument(descriptor_arg):
print('\n\nINSIDE the function `descriptor_arg=my_instance.x`results in: {}'.format(descriptor_arg))
print('`type(descriptor_arg)`: {}'.format(type(descriptor_arg)))
print('Changeing `my_instance.x` value results in:')
descriptor_arg = 5
print('INSIDE the function after changeing `my_instance.x` = {}\n\n'.format(descriptor_arg))
def func_with_descriptor_as_argument(descriptor):
print('\n\nINSIDE the function `descriptor_arg=my_instance.x`results in: {}'.format(descriptor.x))
print('`type(descriptor_arg)`: {}'.format(type(descriptor.x)))
print('Changeing `my_instance.x` value results in:')
descriptor.x = 5
print('INSIDE the function after changeing `my_instance.x` = {}\n\n'.format(descriptor.x))
if __name__ == '__main__':
x_ref = DescriptorReference(instance=my_instance,
descriptor_name=my_instance.__class__.__dict__['x'].__name__)
print('\n\nOUTSIDE the function `my_instance.x`: {}'.format(my_instance.x))
print('Changeing `my_instance.x` value results in:')
my_instance.x = 5
print('OUTSIDE the function after changeing `my_instance.x` = {}\n\n'.format(my_instance.x))
print('Reset:')
my_instance.x = 10
func_with_descriptor_as_argument(descriptor=my_instance.x_ref)
print('OUTSIDE the function after changeing INSIDE the function `my_instance.x` = {}\n\n'.format(my_instance.x))
现在通常您调用my_instance.x
来运行描述符并获取/设置它的全局字典条目my_instance.__dict__['x']
的值并执行所有其他描述符的工作/工作。 通过将带有不存在属性调用的my_instance
作为参数传递给 function,例如func_...(descriptor=my_instance.x_ref)
, my_instance
在其__dict__
中未找到x_ref
后调用__getattr_()
。 然后__getattr__()
生成DecriptorReference()
并将其返回给 function 中的descriptor
参数。 在 function 中,您现在拥有完整的描述符功能,可以操作my_instance
的全局字典。 所以 function 内部的调用descriptor.x
与my_instance.x
外部的 my_instance.x 完全相同。
注意:在此实现中,您可以调用descriptor
上的任何属性,它将与my_instance.x
相同,例如descriptor.made_my_day
。 如果不希望这样做,可以使用if name == 'x':
在DescriptorReference.__getattr__()
中return
之前轻松更改
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.