6
\$\begingroup\$

I only program by hobby, and I'm trying to improve with Python, so along with practicing coding I decided to venture into multiprocessing. This is a command line and will sort a variable number of list, timing itself. I also tried to make sure I followed all PEP8 rules.

#!/usr/bin/env python
# Random Sort
# supernova2468


import sys
import time
import random
import math
import multiprocessing as mp
import argparse


def go_time():
    parser = argparse.ArgumentParser(
        description='Makes statistics on how terrible random sorting is.')
    parser.add_argument('start', help='what length of list to start at',
                        type=int)
    parser.add_argument('end', help='what length of list to end at', type=int)
    parser.add_argument('-t', '--tries',
                        help='specify number of tries per length default: 100',
                        type=int, default=100)
    parser.add_argument('-c', '--cores',
                        help='specify number of cores to use default: 1',
                        type=int)
    parser.add_argument('-v', '--verbose',
                        help='increase output, doesn\'t '
                        'really work in -c mode',
                        action='count')
    args = parser.parse_args()

    start = args.start
    end = args.end
    tries = args.tries
    if args.cores:
        cores = args.cores
        switch = 1
    else:
        switch = 2
        cores = 1

    if args.verbose >= 1:
        print 'Checking Parameters..'
    if end <= start:
        print 'ERROR: Must start before ending'
        sys.exit()
    if tries < 1:
        print 'ERROR: Must have at least one interation'
        sys.exit()
    if tries < cores:
        print 'ERROR: Must have at least one try per core'
        sys.exit()
    if end < 0:
        print 'ERROR: No negative numbers'
        sys.exit()

    print 'Beginning Trials..'

    program_start = time.time()

    if switch == 1:
        try:
            if args.verbose >= 1:
                print 'Starting Pool'
                print ('Warning: Multiple Process Output '
                       'Causes Console Confusion')

            mp.freeze_support()
            pool = mp.Pool(processes=cores)
            split_tries = math.ceil(tries/cores)
            result_list = []
            split_stat_list = []
            stat_list = {}

            if args.verbose >= 2:
                print 'split tries {}'.format(split_tries)

            for i in range(cores):
                if args.verbose >= 1:
                    print 'Process {}'.format(i)
                result_list.append(
                    pool.apply_async(
                        r_sort, (start, end, split_tries, args.verbose)))

            for r in result_list:
                split_stat_list.append(r.get(99999))
            pool.close()

            for single_dict in split_stat_list:
                for c in range(start, end+1):
                    try:
                        stat_list[c] += single_dict[c]
                    except KeyError:
                        stat_list[c] = single_dict[c]

        except KeyboardInterrupt:
            pool.terminate()
            sys.exit()

    elif switch == 2:
        if args.verbose >= 1:
            print 'Starting Single'
        stat_list = r_sort(start, end, tries, args.verbose)

    program_end = time.time()

    print 'Avg Results'
    print 'len {:^10} {:^10}'.format('time', 'attempts')
    print '---|{:-<10}|{:-<10}'.format('', '')

    for l in range(start, end+1):
        num2 = stat_list[l][0]/tries
        num3 = stat_list[l][1]/tries
        print '{:>3}|{:^10}|{:^10}'.format(l, round(num2, 8), num3)
    print 'Total Time: {}'.format(program_end-program_start)


def r_sort(start, end, tries, verbose):
    random.seed()
    current_length = start
    stat_list = {}

    while current_length < end+1:
        i = 0
        time_total = 0
        attempts_total = 0

        while i < tries:
            # actual sorting
            the_list = range(current_length)
            rand_list = list(the_list)
            random.shuffle(rand_list)

            if verbose == 1:
                str_width = str(len(str(tries)))
                print ('List Length {} current_length '
                       '{:0>{width}}/{:0>{width}}\r'
                       .format(current_length,
                               i+1, tries, width=str_width),)

            start_time = time.time()
            attempts = 1

            while the_list != rand_list:
                random.shuffle(rand_list)
                if verbose >= 2:
                    print 'list: {} | {}'.format(rand_list,
                                                 mp.current_process().pid)
                attempts += 1

            stop_time = time.time()

            time_total += stop_time-start_time
            attempts_total += attempts

            i += 1

        stat_list[current_length] = [time_total, attempts_total]
        if verbose == 1:
            print ''
        current_length += 1

    return stat_list

if __name__ == '__main__':
    go_time()
\$\endgroup\$

1 Answer 1

4
\$\begingroup\$

Good job using ArgumentParser.

It is a very, very nice tool that should be used more often in Python.


Near the top of your code where you are doing a bunch of checks on parameters, you have a sys.exit call if the user did something wrong when entering parameters.

However, you don't pass in any exit number.

The method sys.exit takes a single parameter that is the exit code. This exit code describes how execution of the program went to external programs.

By practice, any non-zero number means that something went wrong. Therefore, I recommending doing one of the following:

  • Return 1 if there was any sort of error in the code.

  • Return 1, 2, 3... for each individual and possible error.

The second one is more preferable because, if you create some documentation for the program, you can include a list of all the possible exit codes and what error they are associated with.


A few times in your code, you have these lines:

if args.verbose >= 1:
    [print statement(s)]

Since these repeat so much, you should create a function probably called print_verbose that takes in a string to print that should only be printed if the user chose for a verbose execution.

Here is what I wrote:

def print_verbose(str):
    if verbose:
        print str

Where verbose is a global variable that was set like this by go_time:

verbose = true if args.verbose >= 1 else false

Hint: This used python's ternary operator.

And, by setting global verbose to a boolean rather than keeping it as a number, you've increased efficiency because you now don't have to do conditional checking upon an integer every time you want to print a string.

Note: this does remove the ability of being able to write args.verbose >= 2, however, in my opinion, if the user wanted verbose output, then they wouldn't mind a few extra lines on their screen.


Your code seems to be following PEP8 very well.

However, while this is not required by PEP8, you do not have any docstrings in your code. Docstrings are always a good practice in python.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.