简体   繁体   中英

Python decorator to time recursive functions properly

I am working in a piece of code for studying purposes, and i want to compare the time required to sort a list using different algorithms. I tried using a decorator but since the mergeSort function is recursive, it gives me the result for each recursion. I want to find a way to summarize the result, if possible. Since i'm very new to decorators i'm not sure what could be done in that case. Is there a way to achieve that goal using a decorator?

import random
import functools
import time


def timeIt(func):
    @functools.wraps(func)
    def newfunc(*args, **kwargs):
        startTime = time.time()
        func(*args, **kwargs)
        elapsedTime = time.time() - startTime
        print('function [{}] finished in {} ms'.format(
            func.__name__, int(elapsedTime * 1000)))
    return newfunc


@timeIt
def mergeSort(L):
    if len(L) > 1:
        mid = len(L) // 2
        left = L[:mid] 
        right = L[mid:]
        mergeSort(left)
        mergeSort(right)
        i = j = k = 0
        while i < len(left) and j < len(right):
            if left[i] < right[j]:
                L[k] = left[i]
                i += 1
            else:
                L[k] = right[j]
                j += 1
            k += 1
        while i < len(left):
            L[k] = left[i]
            i += 1
            k += 1
        while j < len(right):
            L[k] = right[j]
            j += 1
            k += 1


@timeIt
def selectionSort(L):
    for fillslot in range(len(L) - 1, 0, -1):
        maxpos = 0
        for location in range(1, fillslot + 1):
            if L[location] > L[maxpos]:
                maxpos = location
        temp = L[fillslot]
        L[fillslot] = L[maxpos]
        L[maxpos] = temp


randomList = random.sample(range(10000), 10000)
mergeSort(randomList.copy())
selectionSort(randomList.copy())

Output:

[...] truncated
function [mergeSort] finished in 7 ms
function [mergeSort] finished in 15 ms
function [mergeSort] finished in 33 ms
function [mergeSort] finished in 68 ms
function [selectionSort] finished in 2049 ms

You could just wrap it with another function...

import random
import functools
import time


def timeIt(func):
    @functools.wraps(func)
    def newfunc(*args, **kwargs):
        startTime = time.time()
        func(*args, **kwargs)
        elapsedTime = time.time() - startTime
        print('function [{}] finished in {} ms'.format(
            func.__name__, int(elapsedTime * 1000)))
    return newfunc



def mergeSort(L):
    if len(L) > 1:
        mid = len(L) // 2
        left = L[:mid]
        right = L[mid:]
        mergeSort(left)
        mergeSort(right)
        i = j = k = 0
        while i < len(left) and j < len(right):
            if left[i] < right[j]:
                L[k] = left[i]
                i += 1
            else:
                L[k] = right[j]
                j += 1
            k += 1
        while i < len(left):
            L[k] = left[i]
            i += 1
            k += 1
        while j < len(right):
            L[k] = right[j]
            j += 1
            k += 1



def selectionSort(L):
    for fillslot in range(len(L) - 1, 0, -1):
        maxpos = 0
        for location in range(1, fillslot + 1):
            if L[location] > L[maxpos]:
                maxpos = location
        temp = L[fillslot]
        L[fillslot] = L[maxpos]
        L[maxpos] = temp


@timeIt
def timedSelectionSort(L):
    selectionSort(L)

@timeIt
def timedMergeSort(L):
    mergeSort(L)


randomList = random.sample(range(10000), 10000)
timedSelectionSort(randomList.copy())
timedMergeSort(randomList.copy())

You can set an attribute ( _entered in the example) on the wrapper function as a flag so that it can tell that it is inside a recursive call if the attribute is set:

def timeIt(func):
    @functools.wraps(func)
    def newfunc(*args, **kwargs):
        if not hasattr(newfunc, '_entered'): # enter only if _entered is not set
            newfunc._entered = True # set _entered
            startTime = time.time()
            func(*args, **kwargs)
            elapsedTime = time.time() - startTime
            print('function [{}] finished in {} ms'.format(
                func.__name__, int(elapsedTime * 1000)))
            del newfunc._entered # remove _entered
    return newfunc

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