繁体   English   中英

如何将实例绑定描述符作为参考参数传递给 function?

[英]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() 我不喜欢它,因为它是一个框架,对任何人都不好。
  • 重载描述符,让它检测它是否被传递给 function ,例如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.xmy_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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM