27

I'm trying to make a simple calculator in Python, using a dictionary. Here's my code:

def default():
    print "Incorrect input!"

def add(a, b):
    print a+b

def sub(a, b):
    print a-b

def mult(a, b):
    print a*b

def div(a, b):
    print a/b

line = raw_input("Input: ")
parts = line.split(" ")
part1 = float(parts[0])
op = parts[1];
part3 = float(parts[2])

dict = {
    '+': add(part1, part3),
    '-': sub(part1, part3),
    '*': mult(part1, part3),
    '/': div(part1, part3)
    }

try:
    dict[op]
except KeyError:
    default()

but all the functions are activated. What's the problem?

Peter Varo
  • 11,726
  • 7
  • 55
  • 77
user3342163
  • 303
  • 1
  • 4
  • 9

3 Answers3

25

Define your dictionary like pairs of the form str : function:

my_dict = {'+' : add, 
           '-' : sub, 
           '*' : mult, 
           '/' : div}

And then if you want to call an operation, use my_dict[op] to get a function, and then pass call it with the corresponding parameters:

 my_dict[op] (part1, part3)
|___________|
      |
  function (parameters)

Note: Don't use Python built-in names as names of variables, or you will hide its implementation. Use my_dict instead of dict for example.

Christian Tapia
  • 33,620
  • 7
  • 56
  • 73
  • 1
    note: `dict` is *not* a Python keyword: `'dict' not in keyword.kwlist`. It is a builtin name though: `'dict' in dir(__builtins__)`. – jfs Feb 23 '14 at 02:23
19

It is because when the dictionary is populated, it executes each operation with the operands, and at the end, you're calling dict[op] which contains None and do nothing with it.

What happens is:

# N.B.: in case this is not clear enough, 
#       what follows is the *BAD* code from the OP
#       with inline explainations why this code is wrong

dict = {
    # executes the function add, outputs the result and assign None to the key '+'
    '+': add(part1, part3), 
    # executes the function sub, outputs the result and assign None to the key '-'
    '-': sub(part1, part3),
    # executes the function mult, outputs the result and assign None to the key '*'
    '*': mult(part1, part3),
    # executes the function div, outputs the result and assign None to the key '/'
    '/': div(part1, part3)
    }

try:
    # gets the value at the key "op" and do nothing with it
    dict[op]
except KeyError:
    default()

which is why you get all outputs, and nothing happens in your try block.

You may want to actually do:

dict = {
    '+': add,
    '-': sub,
    '*': mult,
    '/': div
    }

try:
    dict[op](part1, part3)
except KeyError:
    default()

but as @christian wisely suggests, you should not use python reserved names as variable names, that could lead you into troubles. And another improvement I advice you todo is to print the result once, and make the functions lambdas:

d = {
    '+': lambda x,y: x+y,
    '-': lambda x,y: x-y,
    '*': lambda x,y: x*y,
    '/': lambda x,y: x/y
    }

try:
    print(d[op](part1, part3))
except KeyError:
    default()

which will return the result and print it

zmo
  • 24,463
  • 4
  • 54
  • 90
  • N.B.: it's nice to comment on a post when downvoting to indicate what could be wrong, and make suggestions. – zmo Feb 23 '14 at 01:09
  • Not a downvoter (because the answer is correct), but I guess you got those `-1` because your solution is almost identical to mine. You just added the `lambda` part. – Christian Tapia Feb 23 '14 at 01:12
  • 1
    well, we actually posted the first part at the same time, so that should'nt be a reason :-) – zmo Feb 23 '14 at 01:14
  • I haven't downvoted but my guess is that your answer contains "bad" code without a proper disclaimer. – jfs Feb 23 '14 at 02:30
1

I think it's clearer to use get(key[, default]) on the dict with the default specified. I prefer to leave Exceptions for exceptional circumstances, and it cuts three lines of code and a level of indentation without being obscure.

def calculator(op, part1, part3):
    return {
        '+': part1+part3,
        '-': part1-part3,
        '*': part1*part3,
        '/': part1/part3
    }.get(op, f"The operation '{op}' is not supported!") # for default if op is not found

print(calculator('-', 3, 1))
print(calculator('--', 3, 1))

output:

2
The operation '--' is not supported!
Milovan Tomašević
  • 6,823
  • 1
  • 50
  • 42