0

I am completely new to python and programming but I am trying to learn it using more practical approach.

What I am trying to do is an exercise for converting different units, e.g. pounds -> kilograms, foot -> meter etc.

I have defined all the functions for different unit pairs:

def kg_to_g(value):     
    return round(value*1000.0,2)
def g_to_kg(value):     
    return round(value/1000.0,2)
def inch_to_cm(value):  
    return round(value*2.54,2)
def cm_to_inch(value):  
    return round(value/2.54,2)
def ft_to_cm(value):    
    return round(value*30.48,2)

etc.

and created a list with names of these functions:

unit_list = ['kg_to_g','g_to_kg','inch_to_cm','cm_to_inch',
         'ft_to_cm','cm_to_ft','yard_to_m','m_to_yard',
         'mile_to_km','km_to_mile','oz_to_g','g_to_oz',
         'pound_to_kg','kg_to_pound','stone_to_kg','kg_to_stone',
         'pint_to_l','l_to_pint','quart_to_l','l_to_quart',
         'gal_to_l','l_to_gal','bar_to_l','l_to_bar']

The program should randomly choose a unit pair(e.g. kg->pounds) and value (e.g. 134.23), and the user will be asked to convert those values.

random_unit = random.choice(unit_list)
lower = 0.1001
upper = 2000.1001
range_width = upper - lower
ranval = round(random.random() * range_width + lower, 2)

When user enters answer, the program should compare answer with the calculations defined by function and tell user if it is a correct answer or wrong answer:

def input_handler(answer):
    if answer == random_unit(ranval):
        label2.set_text("Correct!")
    else:
        label2.set_text("Wrong!")

Unfortunately, that way program doesn't work, and codesculptor(codesculptor.org) returns with an error

TypeError: 'str' object is not callable

Could someone please explain to me what is wrong with the code and suggest something to solve the problem.

martineau
  • 119,623
  • 25
  • 170
  • 301
  • 1
    Add `random_unit = globals()[random_unit]` after `random_unit = random.choice(unit_list)`. – Aran-Fey Aug 20 '16 at 13:16
  • `random_unit = random.choice(unit_list)`: this line will assign a random item of `unit_list` (which is a list of `str`) to `random_unit`. You then call `random_unit(ranval)`, so you're using an `str` like a function (a callable is another name for function). – kjaquier Aug 20 '16 at 13:18

2 Answers2

0

Because you've enclosed the function names (in the list) in quotes, they have become strings.

Change your list to:

unit_list = [kg_to_g, g_to_kg, inch_to_cm, cm_to_inch,
         ft_to_cm, cm_to_ft, yard_to_m, m_to_yard,
         mile_to_km, km_to_mile, oz_to_g, g_to_oz,
         pound_to_kg, kg_to_pound, stone_to_kg, kg_to_stone,
         pint_to_l, l_to_pint, quart_to_l, l_to_quart,
         gal_to_l, l_to_gal, bar_to_l, l_to_bar]

And now it is a list of functions, which can be called like this: unit_list[0](34), for example.

So now random_unit(ranval) should not throw an exception.

Note also that comparing floats (if answer == random_unit(ranval)) will most likely cause you problems. See Is floating point math broken? for some detailed explanations of why this is.

As you are rounding you may get away with it, but it's good to be aware of this and understand that you need to deal with it in your code.

Community
  • 1
  • 1
SiHa
  • 7,830
  • 13
  • 34
  • 43
  • `getattr` won't be required here? – Ejaz Aug 20 '16 at 13:46
  • @PEJK No, not if the functions are hard-coded in the list, like this. If you have the name of a function in a variable and want to call it, then yes, `getattr()` is your friend, but it is not necessary here. When the list is defined, the elements are references to the functions themselves. – SiHa Aug 20 '16 at 13:50
  • This solution worked for me! Thank you! My fault was that I defined my unit pair functions AFTER I created a list of unit pairs - that led to an error stating that function was not defined. – Aleksandr Zakirov Aug 20 '16 at 14:09
  • But now given an element from that list (`unit_list[2]` for example), how do you find out its name so you know what units it is converting? – Spacedman Aug 20 '16 at 14:12
-1

I think this is what you are asking about. You should be able to store the functions in a list like this

unit_list = [kg_to_g, g_to_kg, inch_to_cm, cm_to_inch, ft_to_cm]

You can then call each item in the list and give it a parameter and it should execute the function for example like this:

unit_list[0](value)
moonboon
  • 55
  • 1
  • 6