简体   繁体   中英

Detect change in value of a Python class attribute and log it

I have a class in python with lot of methods as follows:

import logging
logger = logging.getLogger(__name__)

class BaseWorkFlow:
   def __init__(self, response=None):
       self.response = response

   def func1(self):
       self.response = 10
       logger.info(self.response.data)

   def func2(self):
       self.response = 20
       logger.info(self.response.data)

   def func3(self):
       self.response = 20
       logger.info(self.response.data)

As is evident, every time the value of self.response changes, I need to log it. Currently i am using explicit logging statements to do this.However, now my code is littered with log statements.Also it is very difficult to update log statements manually when new methods are added.

Is there any way of writing a function or a decorator that automatically does the logging operation anytime self.response is assigned a new value in any of the class methods ?

One possible approach is to use property (properties are implemented with descriptors in Python so you may write your own descriptor to get more powerful and flexible attribute access control):

class BaseWorkFlow:

    def __init__(self, response=None):
        self._response = response

    @property
    def response(self):
        return self._response

    @response.setter
    def response(self, value):
        self._response = value
        # logging after setting
        print(f'self._response = {self._response}')

    def func1(self):
        self.response = 10

    def func2(self):
        self.response = 20

    def func3(self):
        self.response = 20

bwf = BaseWorkFlow()
bwf.func1()
bwf.response = 101

Output:

self._response = 10
self._response = 101

You can achieve it by separating your code on updating the response value and logging.

import logging
logger = logging.getLogger(__name__)

class BaseWorkFlow:

    def __init__(self, response=None):
        self.response = response

    def func1(self):
        self.update_response(10)

    def func2(self):
        self.update_response(20)

    def func3(self):
        self.update_response(20)

    def update_response(response_value):
        self.response = response_value
        logger.info(selfl.response.data)

Use __setattr__ to catch changing value of class attribute.

import logging
logger = logging.getLogger(__name__)

class BaseWorkFlow:
    def __init__(self, response=None):
        self.response = response

    def __setattr__(self, name, value):
        if name == 'response':
            logger.info('{} is set to {}'.format(name, value))

    def func1(self):
        self.response = 10

    def func2(self):
        self.response = 20

    def func3(self):
        self.response = 30

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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