How to create instances of Python descriptor in a decorator?

118 Views Asked by At

I am trying to write a Python program where I catch updates to attributes, so I can later update GUI for all changes or do other processing. Right now, I am using instances of a class that implements the descriptor protocol to catch all changes in observed attributes, which is working well.

What I'd like to change/improve is to move the instances of the ObservedAccess, as shown below in the class TestObserving, into a decorator, something like this: @observableclass(some_int, some_string) class TestObserving

And create the needed instances of ObservedAccess in the decorator.

Thanks for any info or ideas, Paul.

# dummy observer method
def changed(instance, private_name):
    print(f'instence: {instance}, attribute: {private_name} changed')

class ObservedAccess:
    def __set_name__(self, owner, name):
        self.public_name = name
        self.private_name = '_' + name

    def __get__(self, obj, object_type=None):
        value = getattr(obj, self.private_name)
        return value

    def __set__(self, obj, value):
        setattr(obj, self.private_name, value)
        # let observers know the instance has changed
        changed(obj, self.private_name)

class TestObserving:
    some_string = ObservedAccess()
    some_int = ObservedAccess()

    def __init__(self):
        self.some_string = "Today is a demo day"
        self.some_int = 5

if __name__ == '__main__':
    t = TestObserving()

I do not even have an idea of how to start with the decorator to do this. I guess my stumbling block is how to pass the "names" of the instances into the decorator function unless I pass them in as true strings, which seems awkward.

1

There are 1 best solutions below

1
Jun-Jie Huang On

Is this what you need?

def changed(instance, private_name):
    print(f'instence: {instance}, attribute: {private_name} changed')

class ObservedAccess:
    def __set_name__(self, owner, name):
        self.public_name = name
        self.private_name = '_' + name

    def __get__(self, obj, object_type=None):
        value = getattr(obj, self.private_name)
        return value

    def __set__(self, obj, value):
        setattr(obj, self.private_name, value)
        # let observers know the instance has changed
        changed(obj, self.private_name)

class observableclass:
    def __init__(self, some_string, some_int):
        self.some_string = some_string
        self.some_int = some_int
        
    def __call__(self, obj):
        def new(cls):
            instance = object.__new__(cls)
            instance.some_string = self.some_string
            instance.some_int = self.some_int
            return instance
        obj.__new__ = new
        return obj
  
@observableclass(some_string='abc', some_int=123)
class TestObserving:
    some_string = ObservedAccess()
    some_int = ObservedAccess()

    # def __init__(self):
    #     self.some_string = "Today is a demo day"
    #     self.some_int = 5

if __name__ == '__main__':
    t = TestObserving()