简体   繁体   English

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

[英]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.它必须由描述符完成。

Question问题

Can I build some kind descriptor that could pass a kind of reference to the function argument which behaves equivalently?我可以构建某种描述符来传递对 function 参数的引用,该参数的行为等效吗?

So far至今

I've been looking at different options:我一直在寻找不同的选择:

  • pass 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.我不喜欢它,因为它是一个框架,对任何人都不好。
  • overloading the descriptor, letting it detect if it is passed to a function with eg inspect , some AST -package and and then build an appropriate reference inside the overloaded __get__() and return it.重载描述符,让它检测它是否被传递给 function ,例如inspect ,一些AST包,然后在重载的__get__()中构建一个适当的引用并返回它。 I may manage the detection part, but I have no clue how the reference could look like?我可以管理检测部分,但我不知道参考的外观如何? Wrap it in another descriptor?将其包装在另一个描述符中?

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!我很高兴有任何想法并期待讨论!

THX in advance!!!提前谢了!!!

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.xmy_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.

相关问题 我如何在python中的函数参数中传递实例属性 - How can i pass instance attribute in function argument in python 与tkinter绑定时如何传递另一个参数? - How can I pass another argument when binding with tkinter? 如何在python中的函数中传递对象的实例作为参数? - How do I pass instance of an object as an argument in a function in python? 将实例作为函数参数传递 - Pass instance as function argument 在装饰器中,如何传递要装饰的函数作为参数? - In a decorator how can I pass the function I'm decorating as an argument? 在Python中,如何在描述符上调用实例方法? - In Python, how can I call an instance method on a descriptor? 如何将使用实例变量的实例方法作为另一个函数的参数传递? - How to pass an instance method, that uses instance variables, as argument to another function? 如何在Python中传递函数乘以常数作为参数 - How can I pass a function multiplied by a constant as argument in Python python - 如何在python pandas管道中将函数作为参数传递给参数 - How can I pass function as argument with parameter in python pandas pipe 如何传递实例变量作为参数,该参数可以在函数中多次调用? - How to pass instance variable as argument which can persist in function for multiple calls?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM