Problem
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()
Solution
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.