Search code examples
pythonattributesdecorator

How to access Attribute from a function within a decorator?


As in the title described, I want to access a attribute within a decorator. See the following example code:

def my_timer(orig_func):
    import time

    @wraps(orig_func)
    def wrapper(*args, **kwargs):
        t1 = time.time()
        result = orig_func(*args, **kwargs)
        t2 = time.time() - t1
        print('{} ran in: {} sec'.format(orig_func, t2))
        return result

    return wrapper

As you can see I have the decorator function def my_timer(). And within it I have another decorator named def wrapper(). Im calling this decorator like this:

@my_logger
@my_timer
def fit(X_train, y_train):
    fitted = logmodel.fit(X_train, y_train)
    fitted.train_y_predicted = fitted.predict(X_train)
    fitted.train_accuracy = np.mean(fitted.train_y_predicted.ravel() == y_train.ravel()) * 100
    fitted.train_confusion_matrix = confusion_matrix(y_train, fitted.train_y_predicted)
    
    return fitted

Now I need to call the attribute t2 from the @my_time decortator which is within the second decorator function def wrapper().

Furthemore Im calling the fit function in this step:

fitted = fit(X_train, y_train)
print()
print('Train Accuracy : ', fitted.train_accuracy,'\n')
print('Train Confusion Matrix :\n %s\n' % (fitted.train_confusion_matrix))
print()

and the displayed result is:

<function fit at 0x000001D3BCEB3F70> ran in: 0.024797439575195312 sec

Train Accuracy :  89.70149253731343 

Train Confusion Matrix :
 [[312  26]
 [ 43 289]]

And I need that time from this print: "<function fit at 0x000001D3BCEB3F70> ran in: 0.024797439575195312 sec" But I dont know how to get it.


Solution

  • I think you can change a little bit your wrapper to add the time to run your function such that :

    def my_timer(orig_func):
        import time
    
        @wraps(orig_func)
        def wrapper(*args, **kwargs):
            t1 = time.time()
            result = orig_func(*args, **kwargs)
            t2 = time.time() - t1
            print('{} ran in: {} sec'.format(orig_func, t2))
            return result, t2
    
        return wrapper
    

    and use it like so

    @my_logger
    @my_timer
    def fit(X_train, y_train):
        fitted = logmodel.fit(X_train, y_train)
        fitted.train_y_predicted = fitted.predict(X_train)
        fitted.train_accuracy = np.mean(fitted.train_y_predicted.ravel() == y_train.ravel()) * 100
        fitted.train_confusion_matrix = confusion_matrix(y_train, fitted.train_y_predicted)
        
        return fitted
    
    fitted, t = fit(X_train, Y_train)
    

    This way, you add t2 to the output of your function by using the decorator. Note that I removed

    runningtime = my_timer(fit)
    

    as it will just create a function that never gets called (thanks juanpa.arrivillaga )