[英]How can I pass an instance binding descriptor as a reference argument to a function?
this here may be a bit tricky with no straight forward solution, but I would also be happy with a more complex solution:如果没有直接的解决方案,这可能有点棘手,但我也会对更复杂的解决方案感到满意:
I have an instance binding data descriptor binding to a global instance that I want to pass as a function argument without being evaluated (ie __get__()
executed).我有一个实例绑定数据描述符绑定到一个全局实例,我想将它作为 function 参数传递而不被评估(即
__get__()
执行)。 In this code example it does not work, and the descriptor passes the current value 10
of type int
to the function argument, instead of itself:在此代码示例中,它不起作用,并且描述符将
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)
The output is: 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
I do understand that it does not work this way.我明白它不是这样工作的。 But what I want is to manipulate the global instance dictionary value
my_instance.__dict__['x_value']
inside the function.但我想要的是在 function 中操作全局实例字典值
my_instance.__dict__['x_value']
。 I have to repeat this with many instances & functions and the actual descriptors are also doing other stuff (in this example it's only printing "I AM..." but in my case it's eg type checking, triggering other processes etc.), so direct dictionary manipulation is undesired.我必须对许多实例和函数重复这一点,并且实际的描述符也在做其他事情(在这个例子中,它只打印“我是......”但在我的例子中,它是例如类型检查、触发其他进程等),所以不希望直接进行字典操作。 It has to be done by the descriptor.
它必须由描述符完成。
Can I build some kind descriptor that could pass a kind of reference to the function argument which behaves equivalently?我可以构建某种描述符来传递对 function 参数的引用,该参数的行为等效吗?
I've been looking at different options:我一直在寻找不同的选择:
my_instance
and the string name x
separately or as tuple and work with getattr()
, setattr()
inside the function.my_instance
和字符串名称x
或作为元组传递,并在 function 中使用getattr()
、 setattr()
。 I don't like it because it's for a framework and not nice for anybody.inspect
, some AST
-package and and then build an appropriate reference inside the overloaded __get__()
and return it.inspect
,一些AST
包,然后在重载的__get__()
中构建一个适当的引用并返回它。 I may manage the detection part, but I have no clue how the reference could look like? In the end the function argument should work inside the function like a directly callable descriptor but getting/setting the global dictionary my_instance.__dict__['x_value']
(and doing all the other stuff mentioned).最后,function 参数应该在 function 中像直接可调用的描述符一样工作,但获取/设置全局字典
my_instance.__dict__['x_value']
(并执行提到的所有其他内容)。
I'm happy for any ideas and looking forward to discuss!我很高兴有任何想法并期待讨论!
I found a quite nice solution, it's a kind-of-class-wrapper that the descriptor-owner-instance my_instance
creates and returns in __getattr__()
if you try to get a non-existing attribute from it.我找到了一个很好的解决方案,它是一种类包装器,如果您尝试从中获取不存在的属性,则描述符所有者实例
my_instance
在__getattr__()
中创建并返回。
Adjusting above example the solution looks like this:调整上面的例子,解决方案看起来像这样:
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))
Now normally you call my_instance.x
to run the descriptor and get/set the value of it's global dictionary entry my_instance.__dict__['x']
and do all the other descriptor stuff/work.现在通常您调用
my_instance.x
来运行描述符并获取/设置它的全局字典条目my_instance.__dict__['x']
的值并执行所有其他描述符的工作/工作。 By passing my_instance
with a non-existing attribute call as argument to the function, eg func_...(descriptor=my_instance.x_ref)
, my_instance
calls __getattr_()
after not finding the x_ref
in its __dict__
.通过将带有不存在属性调用的
my_instance
作为参数传递给 function,例如func_...(descriptor=my_instance.x_ref)
, my_instance
在其__dict__
中未找到x_ref
后调用__getattr_()
。 Then __getattr__()
generates the DecriptorReference()
and return it to descriptor
argument in the function.然后
__getattr__()
生成DecriptorReference()
并将其返回给 function 中的descriptor
参数。 Inside the function you have now full descriptor functionality which is manipulating the global dictionary of my_instance
.在 function 中,您现在拥有完整的描述符功能,可以操作
my_instance
的全局字典。 So the call descriptor.x
inside the function does absolutely the same as my_instance.x
outside the function.所以 function 内部的调用
descriptor.x
与my_instance.x
外部的 my_instance.x 完全相同。
Note: In this implementation you can call any attribute on descriptor
and it will be identical to my_instance.x
, eg descriptor.made_my_day
.注意:在此实现中,您可以调用
descriptor
上的任何属性,它将与my_instance.x
相同,例如descriptor.made_my_day
。 If this is not desired it can be easily changed with a if name == 'x':
before the return
in DescriptorReference.__getattr__()
如果不希望这样做,可以使用
if name == 'x':
在DescriptorReference.__getattr__()
中return
之前轻松更改
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.