# Command-line-based capacitance decoding

I am an amateur programmer and have just written a simple Capacitor Identifier mostly to practice. I have not had many people review my coding and thought this would be a good time to start. It is completed and works. Is there a better way I should have went about it? What should I look at in what and how I am writing that might improve my approach?

#!/usr/bin/env python3

import sys

# Get the arguments list
cmdargs = sys.argv

cmdargs = cmdargs[1:]

tolerances = {'B':'.10%','C':'.25%','D':'.5%','E':'.5%','F':'1%','G':'2%','H':'3%','J':'5%','K':'10%','M':'20%','N':'.05%','P':'+100% to 0%','Z':'+80% to -20%'}

# Get the total number of args passed
total = len(cmdargs)

# Templete String for output
capTemp = '{} = {}'

# output container
result = []

# cycle through passed arguments and decode each capacitor
for arg in cmdargs:
case = len(arg)

if(case == 2):
temp = arg + ' pf, ' + str(float(arg) / 1000000) + 'uf'
result.append(capTemp.format(arg, temp))
elif(case == 3):
cap = int(arg[:2]) * 10**int(arg[2:])
temp = str(cap) + ' pf, ' + str(float(cap) / 1000000) + 'uf'
result.append(capTemp.format(arg, temp))

elif(case == 4):
cap = int(arg[:2]) * 10**int(arg[2:3])
temp = str(cap) + ' pf, ' + str(float(cap) / 1000000) + 'uf, ' + tolerances[arg[3:].upper()] + ' tolerance'
result.append(capTemp.format(arg, temp))

# print the decoded capacitor values
for i in result:
print(i)


## Variable usage

Assign cmdargs once, and get it right the first time. Put a more insightful comment there. Rename it to cmd_args for readability.

# Skip argv, which is the name of this program
cmd_args = sys.argv[1:]


The total variable is never used.

The variable name case is not descriptive enough. Naming a variable temp is almost always a bad idea as well. I suggest names like arg_len and decoded_value, respectively.

tolerances is a constant, and should be named TOLERANCES by convention. Format it using one entry per line for readability.

## Decoding technique

It is good practice to separate your calculations from your output routines. Start by defining a function decode_capacitance(code):

def decode_capacitance(code):
'''
Decodes the capacitance as a (value, tolerance) pair.
The value is a float representing the nominal number of picofarads.
The tolerance is a free-form string, and may be None.
'''
code_length = len(code)
if code_length == 2:
return float(code), None
elif code_length == 3:
return float(code[:2]) * 10 ** int(arg[2:]), None
elif code_length == 4:
return float(code[:2]) * 10 ** int(arg[2:3]), TOLERANCES[code[3:].upper()]


In Python, it is idiomatic to write if conditions without parentheses.

However, there is a lot of redundancy there, and not much insight either. An improvement would be:

def decode_capacitance(code):
'''(Same docstring)'''
base_pF, scale, tol = float(code[0:2]), int(code[2:3] or 0), code[3:4]
multiplier = 10 ** scale
return base_pF * multiplier, TOLERANCES.get(tol.upper())


You fail to interpret the third digit properly. If the third digit is 8 or 9, it is supposed to be interpreted as a multiplier of 0.01 or 0.1, respectively. To fix that, change the middle line to:

    multiplier = 10 ** (scale if scale <= 5 else scale - 10)


As @vnp noted, you have no validation or error handling (and I haven't added any for you).

## Formatting technique

Define another function to display the capacitance and tolerance in the desired format.

It would be nice, I think, to automatically select the most natural units.

The standard symbol for the farad is F — a capital letter.

## Putting it all together

With the appropriate functions defined, the main loop is quite simple. Print results as you go; there's no need to store results.

import sys

TOLERANCES = {
'B': '.10%',
'C': '.25%',
'D': '.5%',
'E': '.5%',
'F': '1%',
'G': '2%',
'H': '3%',
'J': '5%',
'K': '10%',
'M': '20%',
'N': '.05%',
'P': '+100% to 0%',
'Z': '+80% to -20%',
}

def decode_capacitance(code):
'''
Decodes the capacitance as a (value, tolerance) pair.
The value is a float representing the nominal number of picofarads.
The tolerance is a free-form string, and may be None.
'''
base_pF, scale, tol = float(code[0:2]), int(code[2:3] or 0), code[3:4]
multiplier = 10 ** int(scale if scale <= 5 else scale - 10)
return base_pF * multiplier, TOLERANCES.get(tol.upper())

def format_capacitance(pF, tolerance):
if pF > 1000000:
mantissa, unit = pF / 1000000, 'uF'
else:
mantissa, unit = pF, 'pF'

output = '{} {}'.format(mantissa, unit)
if tolerance is not None:
output += ', ' + tolerance + ' tolerance'
return output

# Skip argv, which is the name of this program
cmd_args = sys.argv[1:]

# Cycle through passed arguments and decode each capacitor
for arg in cmd_args:
print('{} = {}'.format(arg, format_capacitance(*decode_capacitance(arg))))

• I plan to add a error handling, a -h & --help arg to provide a help output, and a read me, i wanted to make a note of you as a contributor to the code, i also plan to study your points heavily, when i complete error handling and the help args, would it be correct to re-edit it or re-post it to get some idea if i am on the right track with it, also need to come up with a simple project to make use of your suggestion and practice them
– John
Oct 15, 2014 at 7:55
• For iterative reviews, please post a separate question. (Thanks for asking about the process!) Oct 15, 2014 at 7:58

Usage

• The code expects a perfect input. A user making a honest mistake is faced with an exception trace. It is nice to detect an error early and print an error message in big friendly letters.

• In the same tune, I am not sure how the valid input should be formatted. It is very well possible that a professional radio engineer knows exactly what is expected; not many of us are though. The usage() function is pretty much a must.

Structure

• Any piece of code you care about should be written with a future reuse in mind. In a Python world it usually means that you'd be able to import it into another project. A habit of factoring the code into well defined functions is immensely helpful.

Misc

cmdargs = sys.argv
cmdargs = cmdargs[1:]


looks really strange. I'd go for

cmdargs = sys.argv[1:]


wrapped in a try/except clause of cource.

• Concerning your last remark, an out-of-bounds list slice is just an empty list. No exception is raised. Oct 15, 2014 at 6:37