簡體   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