4
$\begingroup$

It was suggested I cross post this question from the main Stack Overflow site as it may be of interest here too

I am tutoring a neighbour's child and we were exploring the int() function before using it with input() - which returns a string. We tried the following:

int(5)
int(5.5)
int('5')
int('5.5')

The first three returned 5 as expected; the last one threw the error

ValueError: invalid literal for int() with base 10: '5.5'

Given the behaviour of the first three lines how do I explain the error to a 14-year old (background = speaks 4 languages but is poor at maths)?

UPDATE

C# exhibits the same behaviour: Convert.ToInt32("5.5"); throws the error

Input string was not in a correct format.

$\endgroup$
2
  • $\begingroup$ He doesn't need to be good at math to understand it, python isn't a programming language for first approach $\endgroup$ Oct 31, 2017 at 8:59
  • $\begingroup$ PS in c# you can do (int)5.5 $\endgroup$ Oct 31, 2017 at 9:00

3 Answers 3

3
$\begingroup$

First the reason for the failure. Function int wants either a number or a string that represents an int (from the Python 3 docs). The first part covers your first two examples and the second part covers your third.

However '5.5' isn't a "string that represents an int", hence the failure. It is, of course, a string that represents a number, but that isn't the same thing.

But

int(eval('5.5'))

will return 5 since eval will return a number that int will be able to work with.

Now, how to explain it. There are two options (at least). The first (worst) is just to tell the kid that the world isn't magic and int only works for some things, but doesn't handle everything.

But it makes more sense to treat it as an exercise in careful reading of specifications. Read what the specification does say and what it doesn't say. Specifications are meant to be precise.

You can make it a lesson about reading the manual when you get confused, or writing test cases to explore the range of possibilities.

You can make it an lesson about searching for an alternate solution (i.e. eval) when something you try fails.

You can make it a lesson about "when you are older and get to design things, spend some time getting it right." I haven't explored whether there is logic behind the decision to not include such a case as your fourth example, but I doubt that it was an oversight.


See the note by @ctrl-alt-delor. Function eval permits the evaluation of arbitrary code, not just simple expressions. It shouldn't be used, for example, with unexamined user input strings. It is safe, however, provided that you have control over the argument in some way.

$\endgroup$
3
  • 1
    $\begingroup$ int(eval(aString)) is very dangerous if aString comes from user input. It will allow the user to execute arbitrary code. $\endgroup$ Oct 26, 2017 at 13:09
  • 1
    $\begingroup$ Excellent point. eval is a very big hammer. $\endgroup$
    – Buffy
    Oct 26, 2017 at 13:14
  • $\begingroup$ I think this might also be well coupled with a lesson on "writing good specifications is hard." You don't really get a sense for just how hard it is until many years into programming, but you can at least explain that spec writers have to make tradeoffs. This might be well coupled with ctrl-alt-delor's example of int("36/5"), which starts to show how hard it can be to draw the line on functionality. $\endgroup$
    – Cort Ammon
    Oct 31, 2017 at 0:33
2
$\begingroup$

It is important to discuss the type system early, as it is the whole basis upon which objects interact. The type system is the species structure of the Python object ecosystem.

Strings interact with string behaviors: + is concatenation, [] is substringing, and *(string,int) causes repetition.

Numbers, in the presence of arithmetic operators, do arithmetic.

The moral: type determines context and therefore regulates the interactions between objects.

The int() cast expects an object that is convertible into an integer. If it is passed a floating point number, it truncates the number towards zero. If it is passed a numerical string containing a valid parseable number, it extracts the number value from the string. If it is passed a boolean, it converts true to 1 and false to 0.

If it is passed something that is not a parseable into a number, it throws a ValueError.

$\endgroup$
2
$\begingroup$

I agree with @Buffy, but also.

  • int(42) "42" → 42 is one step: it converts string to int or throws.
  • int(7.2) 7.2 → 7 is one step: it converts float/real to int (and rounds).
  • int("7.2") "7.2" → 7.2 → 7✗ is two steps: convert string to float, and float to int.
  • int("36/5") What about this? Should it be able to do this.

The problem that is causing the confusion is that we have effectively two functions. int taking a string, and int taking a float. They both do different things.

$\endgroup$
2
  • 1
    $\begingroup$ @EllenSpertus, thanks I have now fixed the answer. It is now 42. $\endgroup$ Oct 27, 2017 at 15:26
  • $\begingroup$ As I said below, type determines context. This is an example of function overloading, which is covert in duck-typed Python and overt in statically typed C++ and Java. $\endgroup$ Oct 28, 2017 at 18:31

Your Answer

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

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