0

如果没有直接的解决方案,这可能有点棘手,但我也会对更复杂的解决方案感到满意:

我有一个实例绑定数据描述符绑定到我想作为函数参数传递而不被评估(即__get__()执行)的全局实例。在此代码示例中,它不起作用,并且描述符将10类型的当前值传递int给函数参数,而不是其自身:

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 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)

输出是:

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

我明白它不是这样工作的。但我想要的是my_instance.__dict__['x_value']在函数内部操作全局实例字典值。我必须对许多实例和函数重复这一点,并且实际的描述符也在做其他事情(在这个例子中,它只打印“我是......”但在我的例子中,它是例如类型检查、触发其他进程等),所以不希望直接进行字典操作。它必须由描述符完成。

问题

我可以构建某种描述符,可以将一种引用传递给行为等效的函数参数吗?

至今

我一直在寻找不同的选择:

  • 单独传递my_instance和字符串名称x或作为元组并在函数内部使用getattr(), setattr()。我不喜欢它,因为它是一个框架,对任何人都不好。
  • 重载描述符,让它检测它是否通过例如inspectsome AST-package 传递给函数,然后在重载的内部构建适当的引用__get__()并返回它。我可以管理检测部分,但我不知道参考的外观如何?将其包装在另一个描述符中?

最后,函数参数应该像直接可调用的描述符一样在函数内部工作,但获取/设置全局字典my_instance.__dict__['x_value'](并执行提到的所有其他内容)。

我很高兴有任何想法并期待讨论!

4

1 回答 1

0

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作为参数传递给函数,例如func_...(descriptor=my_instance.x_ref),在其. 然后生成并将其返回给函数中的参数。在函数内部,您现在拥有完整的描述符功能,可以操作. 所以函数内部的调用和函数外部的调用完全一样my_instance__getattr_()x_ref__dict____getattr__()DecriptorReference()descriptormy_instancedescriptor.xmy_instance.x

注意:在这个实现中,您可以调用任何属性descriptor,它将与 相同my_instance.x,例如descriptor.made_my_day。如果不希望这样做,可以在inif name == 'x':之前使用 a 轻松更改returnDescriptorReference.__getattr__()

于 2019-11-13T10:46:40.633 回答