w3resource

Python Attribute Logging Metaclass: Monitor Accesses

Python Metaprogramming: Exercise-4 with Solution

Attribute Logging Metaclass:

Write a Python metaclass “AttrLoggingMeta” that logs every time an attribute is accessed or modified.

Sample Solution:

Python Code :

# Define a metaclass AttrLoggingMeta
class AttrLoggingMeta(type):
    # Override the __new__ method to log attribute accesses
    def __new__(cls, name, bases, dct):
        # Iterate through class attributes
        for key, value in dct.items():
            # Exclude special methods
            if not key.startswith('__'):
                # Replace attribute with a logged version
                dct[key] = cls.log_access(key, value)
        # Call superclass's __new__ method to create the class
        return super().__new__(cls, name, bases, dct)
    
    # Define a static method to log attribute accesses
    @staticmethod
    def log_access(name, value):
        # If the attribute is a method, wrap it to log method calls
        if callable(value):
            def wrapper(*args, **kwargs):
                print(f"Calling method {name}")
                return value(*args, **kwargs)
            return wrapper
        else:
            # If the attribute is a property, log read and write operations
            return property(
                # Log attribute read operation
                fget=lambda self: AttrLoggingMeta.log_read(name, value, self),
                # Log attribute write operation
                fset=lambda self, v: AttrLoggingMeta.log_write(name, v, self)
            )
    
    # Define a static method to log attribute reads
    @staticmethod
    def log_read(name, value, instance):
        print(f"Reading attribute {name}")
        return value
    
    # Define a static method to log attribute writes
    @staticmethod
    def log_write(name, value, instance):
        print(f"Writing attribute {name} with value {value}")
        # Update instance dictionary with the new value
        instance.__dict__[name] = value

# Define a class LoggedClass using AttrLoggingMeta as its metaclass
class LoggedClass(metaclass=AttrLoggingMeta):
    # Define a class attribute foo
    foo = 42

    # Define a class method bar
    def bar(self):
        return 'baz'

# Create an instance of LoggedClass
instance = LoggedClass()
# Access and print the value of the foo attribute
print(instance.foo)  # Reading attribute foo
# Set the value of the foo attribute
instance.foo = 78    # Writing attribute foo with value 84
# Call the bar method
instance.bar()       # Calling method bar 

Output:

Reading attribute foo
42
Writing attribute foo with value 78
Calling method bar

Explanation:

  • Metaclass AttrLoggingMeta:
    • This metaclass is responsible for logging attribute accesses of classes that use it.
    • The "new" method is overridden to intercept attribute assignments and replace them with logged versions.
    • Inside "new", it iterates over the class attributes (excluding special methods) and replaces them with logged versions using the 'log_access' method.
  • Static Method log_access:
    • This method is called to replace each attribute with a logged version.
    • If the attribute is a method, it wraps it with a wrapper function that logs method calls.
    • If the attribute is a property, it replaces it with a property that logs read and write operations.
  • Static Methods log_read and log_write:
    • These methods are called to log read and write operations of properties.
    • They print messages indicating the attribute name and the operation being performed.
  • Class LoggedClass:
    • This class is an example demonstrating the use of the "AttrLoggingMeta" metaclass.
    • It contains a class attribute 'foo' and a method 'bar'.
  • Instance Creation and Testing:
    • An instance of 'LoggedClass' is created.
    • The value of the 'foo' attribute is accessed, demonstrating logging of attribute reads.
    • The value of the 'foo' attribute is changed, demonstrating logging of attribute writes.
    • The "bar" method is called, demonstrating logging of method calls.

Python Code Editor :

Have another way to solve this solution? Contribute your code (and comments) through Disqus.

Previous: Python Singleton Metaclass: Ensure One Instance.
Next: Python Dynamic Class Creation: Flexibility Unleashed

What is the difficulty level of this exercise?

Test your Programming skills with w3resource's quiz.



Follow us on Facebook and Twitter for latest update.