From charlesreid1

(Created page with "Advanced decorator usage: https://www.codementor.io/sheena/advanced-use-python-decorators-class-function-du107nxsv Pretty mind-blowing use of a CLASS decorator, which takes o...")
 
(Automated edit (via update-page on MediaWiki MCP Server))
 
(2 intermediate revisions by the same user not shown)
Line 1: Line 1:
Advanced decorator usage: https://www.codementor.io/sheena/advanced-use-python-decorators-class-function-du107nxsv


Pretty mind-blowing use of a CLASS decorator, which takes one type of class in and returns another type of class out, and wraps the original class with a new one. This uses some advanced features of python classes as well to provide a transparent object wrapper.


In this case, when the user accesses attributes of the wrapper class, it first tries to return that attribute from the wrapper class; if that fails, it tries to return that attribute from the wrapped object. If the attribute is a function, it wraps that function handle with a decorator.
== Overview ==


In effect, this wraps every single method implemented in the wrapped object, and decorates it with a timing method - which saves the user from having to copypaste a bunch of decorators (or worse, copypasta a bunch of code!).
This page demonstrates a two-level decorator pattern in Python: a '''function decorator''' (<code>time_this</code>) combined with a '''class decorator''' (<code>time_all_class_methods</code>) that transparently wraps every method of a class. The technique uses <code>__getattribute__</code> interception to make the wrapper behave like the original object while injecting timing instrumentation.
 
'''Reference:''' Advanced decorator usage: https://web.archive.org/web/20201112033055/https://codementor.io/@sheena/advanced-use-python-decorators-class-function-du107nxsv
 
== How It Works ==
 
=== <code>time_this</code> — Function-level decorator ===
 
A standard decorator that wraps a function with timing logic: it records the wall-clock time before and after the call, prints the elapsed time, and returns the original result unchanged.
 
=== <code>time_all_class_methods</code> — Class-level decorator ===
 
This decorator takes a class and returns a new wrapper class. When instantiated, the wrapper creates an internal instance of the original class (<code>self.oInstance</code>). It overrides <code>__getattribute__</code> to intercept every attribute access:
 
# '''First''', it tries to return the attribute from the wrapper itself (via <code>super().__getattribute__</code>).
# '''If that fails''' with an <code>AttributeError</code>, it retrieves the attribute from the wrapped instance.
# '''If the retrieved attribute is an instance method''', it wraps it with <code>time_this</code> before returning it.
 
This means '''every method call on the wrapper is automatically timed''' — the caller does not need to decorate each method individually.
 
== Code ==
 
<syntaxhighlight lang="python">
def time_this(original_function):
    """Decorator: prints the wall-clock time spent in the decorated function."""
    print("decorating")
    def new_function(*args, **kwargs):
        print("starting timer")
        import datetime
        before = datetime.datetime.now()
        x = original_function(*args, **kwargs)
        after = datetime.datetime.now()
        print("Elapsed Time = {0}".format(after - before))
        return x
    return new_function


<pre>
def time_this(original_function):     
    print "decorating"                     
    def new_function(*args,**kwargs):
        print "starting timer"     
        import datetime               
        before = datetime.datetime.now()                   
        x = original_function(*args,**kwargs)               
        after = datetime.datetime.now()                     
        print "Elapsed Time = {0}".format(after-before)     
        return x                                           
    return new_function 


def time_all_class_methods(Cls):
def time_all_class_methods(Cls):
    """Class decorator: wraps every instance method of Cls with time_this."""
     class NewCls(object):
     class NewCls(object):
         def __init__(self,*args,**kwargs):
         def __init__(self, *args, **kwargs):
             self.oInstance = Cls(*args,**kwargs)
             self.oInstance = Cls(*args, **kwargs)
         def __getattribute__(self,s):
 
         def __getattribute__(self, s):
             """
             """
             this is called whenever any attribute of a NewCls object is accessed. This function first tries to
             Intercept every attribute access on the wrapper.
             get the attribute off NewCls. If it fails then it tries to fetch the attribute from self.oInstance (an
             1. Try the wrapper's own attributes first.
             instance of the decorated class). If it manages to fetch the attribute from self.oInstance, and
             2. Fall back to the wrapped instance (self.oInstance).
             the attribute is an instance method then `time_this` is applied.
             3. If the attribute is an instance method, apply time_this.
             """
             """
             try:  
             try:
                 x = super(NewCls,self).__getattribute__(s)
                 x = super(NewCls, self).__getattribute__(s)
             except AttributeError:    
             except AttributeError:
                 pass
                 pass
             else:
             else:
                 return x
                 return x
             x = self.oInstance.__getattribute__(s)
             x = self.oInstance.__getattribute__(s)
             if type(x) == type(self.__init__): # it is an instance method
 
                 return time_this(x)                 # this is equivalent of just decorating the method with time_this
            # Check if the attribute is an instance method
             if type(x) == type(self.__init__):
                 return time_this(x)
             else:
             else:
                 return x
                 return x
     return NewCls
     return NewCls


#now lets make a dummy class to test it out on:


# ---- Example usage ----
@time_all_class_methods
@time_all_class_methods
class Foo(object):
class Foo(object):
     def a(self):
     def a(self):
         print "entering a"
         print("entering a")
         import time
         import time
         time.sleep(3)
         time.sleep(3)
         print "exiting a"
         print("exiting a")
 


oF = Foo()
oF = Foo()
oF.a()
oF.a()
</syntaxhighlight>
== Key Points ==


</pre>
* The wrapper class is ''transparent'': accessing <code>oF.some_attr</code> behaves as if you were accessing the original <code>Foo</code> instance.
* <code>time_this</code> is applied ''lazily'' — only when a method is accessed, not at wrapper-creation time.
* This pattern is useful for '''cross-cutting concerns''' like logging, profiling, access control, or memoization that should apply to every method of a class without repetitive per-method decorator boilerplate.
{{CSFlag}}

Latest revision as of 14:08, 16 June 2026


Overview

This page demonstrates a two-level decorator pattern in Python: a function decorator (time_this) combined with a class decorator (time_all_class_methods) that transparently wraps every method of a class. The technique uses __getattribute__ interception to make the wrapper behave like the original object while injecting timing instrumentation.

Reference: Advanced decorator usage: https://web.archive.org/web/20201112033055/https://codementor.io/@sheena/advanced-use-python-decorators-class-function-du107nxsv

How It Works

time_this — Function-level decorator

A standard decorator that wraps a function with timing logic: it records the wall-clock time before and after the call, prints the elapsed time, and returns the original result unchanged.

time_all_class_methods — Class-level decorator

This decorator takes a class and returns a new wrapper class. When instantiated, the wrapper creates an internal instance of the original class (self.oInstance). It overrides __getattribute__ to intercept every attribute access:

  1. First, it tries to return the attribute from the wrapper itself (via super().__getattribute__).
  2. If that fails with an AttributeError, it retrieves the attribute from the wrapped instance.
  3. If the retrieved attribute is an instance method, it wraps it with time_this before returning it.

This means every method call on the wrapper is automatically timed — the caller does not need to decorate each method individually.

Code

def time_this(original_function):
    """Decorator: prints the wall-clock time spent in the decorated function."""
    print("decorating")
    def new_function(*args, **kwargs):
        print("starting timer")
        import datetime
        before = datetime.datetime.now()
        x = original_function(*args, **kwargs)
        after = datetime.datetime.now()
        print("Elapsed Time = {0}".format(after - before))
        return x
    return new_function


def time_all_class_methods(Cls):
    """Class decorator: wraps every instance method of Cls with time_this."""
    class NewCls(object):
        def __init__(self, *args, **kwargs):
            self.oInstance = Cls(*args, **kwargs)

        def __getattribute__(self, s):
            """
            Intercept every attribute access on the wrapper.
            1. Try the wrapper's own attributes first.
            2. Fall back to the wrapped instance (self.oInstance).
            3. If the attribute is an instance method, apply time_this.
            """
            try:
                x = super(NewCls, self).__getattribute__(s)
            except AttributeError:
                pass
            else:
                return x

            x = self.oInstance.__getattribute__(s)

            # Check if the attribute is an instance method
            if type(x) == type(self.__init__):
                return time_this(x)
            else:
                return x

    return NewCls


# ---- Example usage ----
@time_all_class_methods
class Foo(object):
    def a(self):
        print("entering a")
        import time
        time.sleep(3)
        print("exiting a")


oF = Foo()
oF.a()

Key Points

  • The wrapper class is transparent: accessing oF.some_attr behaves as if you were accessing the original Foo instance.
  • time_this is applied lazily — only when a method is accessed, not at wrapper-creation time.
  • This pattern is useful for cross-cutting concerns like logging, profiling, access control, or memoization that should apply to every method of a class without repetitive per-method decorator boilerplate.





See also: