1 Numeric types

Whether you’re calculating salaries, bank interest, or cellular frequencies, it’s hard to imagine a program that doesn’t use numbers in one way or another. Python has three different numeric types: int, float, and complex. For most of us, it’s enough to know about (and work with) int (for whole numbers) and float (for numbers with a fractional component).

Numbers are not only fundamental to programming, but also give us a good introduction to how a programming language operates. Understanding how variable assignment and function arguments work with integers and floats will help you to reason about more complex types, such as strings, tuples, and dicts.

This chapter contains exercises that work with numbers, as inputs and as outputs. Although working with numbers can be fairly basic and straightforward, converting between them, and integrating them with other data types, can sometimes take time to get used to.

Useful references

Table 1.1 What you need to know

Concept

What is it?

Example

To learn more

random

Module for generating random numbers and selecting random elements

number = random.randint(1, 100)

http://mng.bz/Z2wj

Comparisons

Operators for comparing values

x < y

http://mng.bz/oPJj

f-strings

Strings into which expressions can be interpolated

f'It is currently {datetime.datetime .now()}'

http://mng.bz/1z6Z and http://mng.bz/PAm2

for loops

Iterates over the elements of an iterable

for i in range(10): print(i*i)

http://mng.bz/Jymp

input

Prompts the user to enter a string, and returns a string

input('Enter your name: ')

http://mng.bz/wB27

enumerate

Helps us to number elements of iterables

for index, item in enumerate('abc'): print(f'{index}: {item}')

http://mng.bz/qM1K

reversed

Returns an iterator with the reversed elements of an iterable

r = reversed('abcd')

http://mng.bz/7XYx

Exercise 1 Number guessing game

This first exercise is designed to get your fingers warmed up for the rest of the book. It also introduces a number of topics that will repeat themselves over your Python career: loops, user input, converting types, and comparing values.

More specifically, programs all have to get input to do something interesting, and that input often comes from the user. Knowing how to ask the user for input not only is useful, but allows us to think about the type of data we’re getting, how to convert it into a format we can use, and what the format would be.

As you might know, Python only provides two kinds of loops: for and while. Knowing how to write and use them will serve you well throughout your Python career. The fact that nearly every type of data knows how to work inside of a for loop makes such loops common and useful. If you’re working with database records, elements in an XML file, or the results from searching for text using regular expressions, you’ll be using for loops quite a bit.

For this exercise

  • Write a function (guessing_game) that takes no arguments.

  • When run, the function chooses a random integer between 0 and 100 (inclusive).

  • Then ask the user to guess what number has been chosen.

  • Each time the user enters a guess, the program indicates one of the following:

    • Too high

    • Too low

    • Just right

  • If the user guesses correctly, the program exits. Otherwise, the user is asked to try again.

  • The program only exits after the user guesses correctly.

We’ll use the randint (http://mng.bz/mBEn) function in the random module to generate a random number. Thus, you can say

import random
number = random.randint(10, 30)

and number will contain an integer from 10 to (and including) 30. We can then do whatever we want with number--print it, store it, pass it to a function, or use it in a calculation.

We’ll also be prompting the user to enter text with the input function. We’ll actually be using input quite a bit in this book to ask the user to tell us something. The function takes a single string as an argument, which is displayed to the user. The function then returns the string containing whatever the user entered; for example:

name = input('Enter your name: ')
print(f'Hello, {name}!')

Note If the user simply presses Enter when presented with the input prompt, the value returned by input is an empty string, not None. Indeed, the return value from input will always be a string, regardless of what the user entered.

Note In Python 2, you would ask the user for input using the raw_input function. Python 2’s input function was considered dangerous, since it would ask the user for input and then evaluate the resulting string using the eval function. (If you’re interested, see http://mng.bz/6QGG.) In Python 3, the dangerous function has gone away, and the safe one has been renamed input.

Working it out

At its heart, this program is a simple application of the comparison operators (==, <, and >) to a number, such that a user can guess the random integer that the computer has chosen. However, several aspects of this program merit discussion.

First and foremost, we use the random module to generate a random number. After importing random, we can then invoke random.randint, which takes two parameters, returning a random integer. In general, the random module is a useful tool whenever you need to choose a random value.

Note that the maximum number in random.randint is inclusive. This is unusual in Python; most of the time, such ranges in Python are exclusive, meaning that the higher number is not included.

Tip The random module doesn’t just generate random numbers. It also has functions to choose one or more elements from a Python sequence.

Now that the computer has chosen a number, it’s the user’s turn to guess what that number is. Here, we start an infinite loop in Python, which is most easily created with while True. Of course, it’s important that there be a way to break out of the loop; in this case, it will be when the user correctly guesses the value of answer. When that happens, the break command is used to exit from the innermost loop.

The input (http://mng.bz/wB27) function always returns a string. This means that if we want to guess a number, we must turn the user’s input string into an integer. This is done in the same way as all conversions in Python: by using the target type as a function, passing the source value as a parameter. Thus int('5') will return the integer 5, whereas str(5) will return the string '5'. You can also create new instances of more complex types by invoking the class as a function, as in list('abc') or dict([('a', 1), ('b', 2), ('c', 3)]).

In Python 3, you can’t use < and > to compare different types. If you neglect to turn the user’s input into an integer, the program will exit with an error, saying that it can’t compare a string (i.e., the user’s input) with an integer.

Note In Python 2, it wasn’t an error to compare objects of different types. But the results you would get were a bit surprising, if you didn’t know what to expect. That’s because Python would first compare them by type, and then compare them within that type. In other words, all integers were smaller than all lists, and all lists were smaller than all strings. Why would you ever want to use < and > on objects of different types? You probably wouldn’t, and I found that this functionality confused people more than it helped them. In Python 3, you can’t make such a comparison; trying to check with 1 < [10, 20, 30] will result in a TypeError exception.

In this exercise, and the rest of this book, I use f-strings to insert values from variables into our strings. I’m a big fan of f-strings and encourage you to try them as well. (See the sidebar discussing f-strings later in this chapter.)

Saved by the walrus

People coming to Python from other languages are often surprised to find while True loops, in which we then trap user input and break. Isn’t there a better way? Some suggest using the following code:

while s = input('Enter thoughts:'):
    print(f'Your thoughts are: {s}')

This makes a lot of sense--we’ll ask the user for their input and assign that to s. However, the value assigned to s will then be passed to while, which will evaluate it as a Boolean. If we get an empty string, then the Boolean value is False, and we exit from the loop.

There’s just one problem with this code: it won’t work. That’s because assignment in Python is not an expression--that is, it doesn’t return a value. If it doesn’t return a value, then it can’t be used in a while loop.

As of Python 3.8, that has changed somewhat. This version introduced the “assignment expression” operator, which looks like := (a colon followed by an equal sign). But no one really calls it the “assignment expression operator”; from early on, it’s been called the “walrus operator.” Also from early on, this operator has been highly controversial. Some people have said that it introduced unnecessary complexity and potential bugs into the language.

Here’s how the previous loop would look in Python 3.8:

while s := input('Enter thoughts:'):
    print(f'Your thoughts are: {s}')

With the walrus operator in the language, we can finally be rid of while True loops and their potential for havoc! But wait--don’t we need to worry about the weird effects of assignment in a while loop’s condition? Maybe, and that’s part of the controversy. But I was convinced, in no small part, by the fact that regular assignment and the assignment operator are not interchangeable; where one can be used, the other cannot. I think that reduces the potential for abuse.

If you want to learn more about the walrus operator, its controversy, and why it’s actually quite useful, I suggest that you watch the following talk from PyCon 2019, in which Dustin Ingram makes an effective case for it: http://mng.bz/nPxv.

You can also read more about this operator in PEP 572, where it was introduced and defined: http://mng.bz/vxOx.

Solution

import random
 
 
def guessing_game():
    answer = random.randint(0, 100)
 
    while True:
        user_guess = int(input('What is your guess? '))
 
        if user_guess == answer:
            print(f'Right!  The answer is {user_guess}')
            break
 
        if user_guess < answer:
            print(f'Your guess of {user_guess} is too low!')
        else:
            print(f'Your guess of {user_guess} is too high!')
 
guessing_game()

You can work through a version of this code in the Python Tutor at http://mng.bz/ vx1q.

Note We’re going to assume, for the purposes of this exercise, that our user will only enter valid data, namely integers. Remember that the int function normally assumes that we’re giving it a decimal number, which means that its argument may contain only digits. If you really want to be pedantic, you can use the str.isdigit method (http://mng.bz/oPVN) to check that a string contains only digits. Or you can trap the ValueError exception you’ll get if you run int on something that can’t be turned into an integer.

Walk through your code using Python Tutor

In this book, I use many diagrams from the Python Tutor (http://mng.bz/2XJX), an amazing online resource for teaching and learning Python. (I often use it in my in-person classes.) You can enter nearly any Python code into the site and then walk through its execution, piece by piece. Most of the solutions in this book have a link pointing to the code in the Python Tutor so that you can run it without typing it into the site.

In the Python Tutor, global variables (including functions and classes) are shown in the global frame. Remember that if you define a variable outside a function, you’ve created a global variable. Any variables you create inside a function are local variables--and are shown, in the Python Tutor, inside their own shaded boxes. Simple data structures, such as integers and strings, are shown alongside the variables pointing to them, whereas lists, tuples, and dicts are shown in graphical format.

Screencast solution

Watch this short video walkthrough of the solution: https://livebook.manning.com/ video/python-workout.

Beyond the exercise

You’ll often be getting input from users, and because it comes as a string, you’ll often need to convert it into other types, such as (in this exercise) integers. Here are some additional ideas for ways to practice this idea:

  • Modify this program, such that it gives the user only three chances to guess the correct number. If they try three times without success, the program tells them that they didn’t guess in time and then exits.

  • Not only should you choose a random number, but you should also choose a random number base, from 2 to 16, in which the user should submit their input. If the user inputs “10” as their guess, you’ll need to interpret it in the correct number base; “10” might mean 10 (decimal), or 2 (binary), or 16 (hexadecimal).

  • Try the same thing, but have the program choose a random word from the dictionary, and then ask the user to guess the word. (You might want to limit yourself to words containing two to five letters, to avoid making it too horribly difficult.) Instead of telling the user that they should guess a smaller or larger number, have them choose an earlier or later word in the dict.

f-strings

Many people, when doing the “number guessing game” exercise, try to print a combination of a string and a number, such as “You guessed 5.” They quickly discover that Python doesn’t allow you to add (using +) strings and integers. How, then, can you include both types in the same line of output?

This problem has long troubled newcomers to Python from other languages. The earliest method was to use the % operator on a string:

'Hello, %s' % 'world'

While C programmers rejoiced at having something that worked like printf, everyone else found this technique to be frustrating. Among other things, % wasn’t super-intuitive for new developers, forced you to use parentheses when passing more than one argument, and didn’t let you reference repeated values easily.

It was thus a vast improvement when the str.format method was introduced into Python, letting us say

'Hello, {0}'.format('world')

Whereas I loved the use of str.format, many newcomers to Python found it a bit hard to use and very long. In particular, they didn’t like the idea of referencing variables on the left and giving values on the right. And the syntax inside of the curly braces was unique to Python, which was frustrating for all.

Python 3.6 introduced f-strings, which are similar to the sort of double-quoted strings programmers in Perl, PHP, Ruby, and Unix shells have enjoyed for decades. f-strings work basically the same way as str.format but without having to pass parameters:

name = 'world'
f'Hello, {name}'

It’s actually even better than that. You can put whatever expression you want inside the curly braces, and it’ll be evaluated when the string is evaluated; for example

name = 'world'
x = 100
y = 'abcd'
f'x * 2 = {x*2}, and y.capitalize() is {y.capitalize()}'

You can also affect the formatting of each data type by putting a code after a colon (:) inside of the curly braces. For example, you can force the string to be aligned left or right, on a field of 10 hash marks (#), with the following:

name = 'world'
first = 'Reuven'
last = 'Lerner'
f'Hello, {first:#<10} {last:#>10}'       

The format code #<10 means that the string should be placed, left-aligned, in a field of 10 characters, with # placed wherever the word doesn’t fill it. The format code #>10 means the same thing, but right-aligned.

I definitely encourage you to take a look at f-strings and to use them. They’re one of my favorite changes to Python from the last few years.

For more information on f-strings, check the following resources:

What if you’re still using Python 2 and can’t use f-strings? Then you can and should still use str.format, a string method that works approximately the same way, but with less flexibility. Plus, you have to call the method, and reference the arguments by number or name.

Exercise 2 Summing numbers

One of my favorite types of exercises involves reimplementing functionality that we’ve seen elsewhere, either inside of Python or in Unix. That’s the background for this next exercise, in which you’ll reimplement the sum (http://mng.bz/MdW2) function that comes with Python. That function takes a sequence of numbers and returns the sum of those numbers. So if you were to invoke sum([1,2,3]), the result would be 6.

The challenge here is to write a mysum function that does the same thing as the built-in sum function. However, instead of taking a single sequence as a parameter, it should take a variable number of arguments. Thus, although you might invoke sum([1,2,3]), you’d instead invoke mysum(1,2,3) or mysum(10,20,30,40,50).

Note The built-in sum function takes an optional second argument, which we’re ignoring here.

And no, you shouldn’t use the built-in sum function to accomplish this! (You’d be amazed just how often someone asks me this question when I’m teaching courses.)

This exercise is meant to help you think about not only numbers, but also the design of functions. And in particular, you should think about the types of parameters functions can take in Python. In many languages, you can define functions multiple times, each with a different type signature (i.e., number of parameters, and parameter types). In Python, only one function definition (i.e., the last time that the function was defined) sticks. The flexibility comes from appropriate use of the different parameter types.

Tip If you’re not familiar with it, you’ll probably want to look into the splat operator (asterisk), described in this Python tutorial: http://mng.bz/aR4J.

Working it out

The mysum function is a simple example of how we can use Python’s “splat” operator (aka *) to allow a function to receive any number of arguments. Because we have prefaced the name numbers with *, we’re telling Python that this parameter should receive all of the arguments, and that numbers will always be a tuple.

Even if no arguments are passed to our function, numbers will still be a tuple. It’ll be an empty tuple, but a tuple nonetheless.

The splat operator is especially useful when you want to receive an unknown number of arguments. Typically, you’ll expect that all of the arguments will be of the same type, although Python doesn’t enforce such a rule. In my experience, you’ll then take the tuple (numbers, in this case) and iterate over each element with either a for loop or a list comprehension.

Note If you’re retrieving elements from *args with numeric indexes, then you’re probably doing something wrong. Use individual, named parameters if you want to pick them off one at a time.

Because we expect all of the arguments to be numeric, we set our output local variable to 0 at the start of the function, and then we add each of the individual numbers to it in a for loop. Once we have this function, we can invoke it whenever we want, on any list, set, or tuple of numbers.

While you might not use sum (or reimplement it) very often, *args is an extremely common way for a function to accept an unknown number of arguments.

Turning iterables into arguments

What if we have a list of numbers, such as [1,2,3], and wish to use mysum with it? We can’t simply invoke mysum([1,2,3]); this will result in the numbers argument being a tuple whose first and only element is the list [1,2,3], which looks like this: ([1,2,3],).

Python will iterate over our one-element tuple, trying to add 0 to [1,2,3]. This will result in a TypeError exception, with Python complaining that it can’t add an integer to a list.

The solution in such a case is to preface the argument with * when we invoke the function. If we call mysum(*[1,2,3]), our list becomes three separate arguments, which will then allow the function to be called in the usual way.

This is generally true when invoking functions. If you have an iterable object and want to pass its elements to a function, just preface it with * in the function call.

Solution

def mysum(*numbers):
    output = 0
    for number in numbers:
            output += number
    return output
 
print(mysum(10, 20, 30, 40))

You can work through this code in the Python Tutor at http://mng.bz/nPQg.

Screencast solution

Watch this short video walkthrough of the solution: https://livebook.manning.com/ video/python-workout.

Beyond the exercise

It’s extremely common to iterate over the elements of a list or tuple, performing an operation on each element and then (for example) summing them. Here are some examples:

  • The built-in version of sum takes an optional second argument, which is used as the starting point for the summing. (That’s why it takes a list of numbers as its first argument, unlike our mysum implementation.) So sum([1,2,3], 4) returns 10, because 1+2+3 is 6, which would be added to the starting value of 4. Reimplement your mysum function such that it works in this way. If a second argument is not provided, then it should default to 0. Note that while you can write a function in Python 3 that defines a parameter after *args, I’d suggest avoiding it and just taking two arguments--a list and an optional starting point.

  • Write a function that takes a list of numbers. It should return the average (i.e., arithmetic mean) of those numbers.

  • Write a function that takes a list of words (strings). It should return a tuple containing three integers, representing the length of the shortest word, the length of the longest word, and the average word length.

  • Write a function that takes a list of Python objects. Sum the objects that either are integers or can be turned into integers, ignoring the others.

Exercise 3 Run timing

System administrators often use Python to perform a variety of tasks, including producing reports from user inputs and files. It’s not unusual to report how often a particular error message has occurred, or which IP addresses have accessed a server most recently, or which usernames are most likely to have incorrect passwords. Learning how to accumulate information over time and produce some basic reports (including average times) is thus useful and important. Moreover, knowing how to work with floating-point values, and the differences between them and integers, is important.

For this exercise, then, we’ll assume that you run 10 km each day as part of your exercise regime. You want to know how long, on average, that run takes.

Write a function (run_timing) that asks how long it took for you to run 10 km. The function continues to ask how long (in minutes) it took for additional runs, until the user presses Enter. At that point, the function exits--but only after calculating and displaying the average time that the 10 km runs took.

For example, here’s what the output would look like if the user entered three data points:

Enter 10 km run time: 15
Enter 10 km run time: 20
Enter 10 km run time: 10
Enter 10 km run time: <enter>
 
Average of 15.0, over 3 runs

Note that the numeric inputs and outputs should all be floating-point values. This exercise is meant to help you practice converting inputs into appropriate types, along with tracking information over time. You’ll probably be tracking data that’s more sophisticated than running times and distances, but the idea of accumulating data over time is common in programs, and it’s important to see how to do this in Python.

Working it out

In the previous exercise, we saw that input is a function that returns a string, based on input from the user. In this case, however, the user might provide two types of input; they might enter a number, but they also might enter the empty string.

Because empty strings, as well as the numeric 0, are considered to be False within an if statement, it’s common for Python programs to use an expression as shown in the solution:

if not one_run:
    break

It’s unusual, and would be a bit weird, to say

if len(one_run) == 0:
    break

Although this works, it’s not considered good Python style, according to generally accepted conventions. Following these conventions can make your code more Pythonic, and thus more readable by other developers. In this case, using not in front of a variable that might be empty, and thus providing us with a False value in this context, is much more common.

In a real-world Python application, if you’re taking input from the user and calling float (http://mng.bz/gyYR), you should probably wrap it within try (http://mng .bz/5aY1), in case the user gives you an illegal value:

try:
    n = float(input('Enter a number: '))
    print(f'n = {n}')
except ValueError as e:
    print('Hey! That's not a valid number!')

Also remember that floating-point numbers are not completely accurate. They’re good enough for measuring the time it takes to run, but they’re a bad idea for any sensitive measurement, such as a scientific or financial calculation.

If you didn’t know this already, then I suggest you go to your local interactive Python interpreter and ask it for the value of 0.1 + 0.2. You might be surprised by the results. (You can also go to http://mng.bz/6QGD and see how this works in other programming languages.)

One common solution for this problem is to use integers. Instead of keeping track of dollars and cents (as a float), you can just keep track of cents (as an int).

Gaining control with f-strings

If you want to print a floating-point number in Python, then you might want to use an f-string. Why? Because in this way, you can specify the number of digits that will be printed out. Here’s an example:

>>> s = 0.1 + 0.7
>>> print(s)
0.7999999999999999

That’s probably not what you want. However, by putting s inside of an f-string, you can limit the output:

>>> s = 0.1 + 0.7
>>> print(f'{s:.2f}')
0.80

Here, I’ve told the f-string that I want to take the value of s and then display it as a floating-point number (f) with a maximum of two digits after the decimal point. See the reference table (table 1.1) at the start of this chapter for the full documentation on f-strings and the formatting codes you can use for different data types.

Solution

def run_timing():
    """Asks the user repeatedly for numeric input. Prints the average time and number of runs."""
 
    number_of_runs = 0
    total_time = 0
 
    while True:                                    
         one_run = input('Enter 10 km run time: ')
 
        if not one_run:                            
             break
 
        number_of_runs += 1
        total_time += float(one_run)
 
    average_time = total_time / number_of_runs
 
    print(f'Average of {average_time}, over {number_of_runs} runs')
 
run_timing()

Look, it’s an infinite loop! It might seem weird to have “while True,” and it’s a very bad idea to have such a loop without any “break” statement to exit when a condition is reached. But as a general way of getting an unknown number of inputs from the users, I think it’s totally fine.

If one_run is an empty string, stop.

You can work through this code in the Python Tutor at http://mng.bz/4A1g.

Screencast solution

Watch this short video walkthrough of the solution: https://livebook.manning.com/ video/python-workout.

Beyond the exercise

Floating-point numbers are both necessary and potentially dangerous in the programming world; necessary because many things can only be represented with fractional numbers, but potentially dangerous because they aren’t exact. You should thus think about when and where you use them. Here are two exercises in which you’ll want to use float:

  • Write a function that takes a float and two integers (before and after). The function should return a float consisting of before digits before the decimal point and after digits after. Thus, if we call the function with 1234.5678, 2 and 3, the return value should be 34.567.

  • Explore the Decimal class (http://mng.bz/oPVr), which has an alternative floating-point representation that’s as accurate as any decimal number can be. Write a function that takes two strings from the user, turns them into decimal instances, and then prints the floating-point sum of the user’s two inputs. In other words, make it possible for the user to enter 0.1 and 0.2, and for us to get 0.3 back.

Exercise 4 Hexadecimal output

Loops are everywhere in Python, and the fact that most built-in data structures are iterable makes it easy to work through them, one element at a time. However, we typically iterate over an object forward, from its first element to the last one. Moreover, Python doesn’t automatically provide us with the indexes of the elements. In this exercise, you’ll see how a bit of creativity, along with the built-in reversed and enumerate functions, can help you to get around these issues.

Hexadecimal numbers are fairly common in the world of computers. Actually, that’s not entirely true; some programmers use them all of the time. Other programmers, typically using high-level languages and doing things such as web development, barely even remember how to use them.

Now, the fact is that I barely use hexadecimal numbers in my day-to-day work. And even if I were to need them, I could use Python’s built-in hex function (http://mng .bz/nPxg) and 0x prefix. The former takes an integer and returns a hex string; the latter allows me to enter a number using hexadecimal notation, which can be more convenient. Thus, 0x50 is 80, and hex(80) will return the string 0x50.

For this exercise, you need to write a function (hex_output) that takes a hex number and returns the decimal equivalent. That is, if the user enters 50, you’ll assume that it’s a hex number (equal to 0x50) and will print the value 80 to the screen. And no, you shouldn’t convert the number all at once using the int function, although it’s permissible to use int one digit at a time.

This exercise isn’t meant to test your math skills; not only can you get the hex equivalent of integers with the hex function, but most people don’t even need that in their day-to-day lives. However, this does touch on the conversion (in various ways) across types that we can do in Python, thanks to the fact that sequences (e.g., strings) are iterable. Consider also the built-in functions that you can use to solve this problem even more easily than if you had to write things from scratch.

Tip Python’s exponentiation operator is **. So the result of 2**3 is the integer 8.

Working it out

A key aspect of Python strings is that they are sequences of characters, over which we can iterate in a for (http://mng.bz/vxOJ) loop. However, for loops in Python, unlike their C counterparts, don’t give us (or even use) the characters’ indexes. Rather, they iterate over the characters themselves.

If we want the numeric index of each character, we can use the built-in enumerate (http://mng.bz/qM1K) function. This function returns a two-element tuple with each iteration; using Python’s multiple-assignment (“unpacking”) syntax, we can capture each of these values and stick them into our power and digit variables.

Here’s an example of how we can use enumerate to print the first four letters of the alphabet, along with the letters’ indexes in the string:

for index, one_letter in enumerate('abcd'):
    print(f'{index}: {one_letter}')

Note Why does Python have enumerate at all? Because in many other languages, such as C, for loops iterate over sequences of numbers, which are used to retrieve elements from a sequence. But in Python, our for loops retrieve the items directly, without needing any explicit index variable. enumerate thus produces the indexes based on the elements--precisely the opposite of how things work in other languages.

You also see the use of reversed (http://mng.bz/7XYx) here, such that we start with the final digit and work our way up to the first digit. reversed is a built-in function that returns a new string whose value is the reverse of the old one. We could get the same result using slice syntax, hexnum[::-1], but I find that many people are confused by this syntax. Also, the slice returns a new string, whereas reversed returns an iterator, which consumes less memory.

We need to convert each digit of our decimal number, which was entered as a string, into an integer. We do that with the built-in int (http://mng.bz/4Ava) function, which we can think of as creating a new instance of the int class or type. We also see that int takes two arguments. The first is mandatory and is the string we want to turn into an integer. The second is optional and contains the number base. Since we’re converting from hexadecimal (i.e., base 16), we pass 16 as the second argument.

Solution

def hex_output():
    decnum = 0
    hexnum = input('Enter a hex number to convert: ')
    for power, digit in enumerate(reversed(hexnum)):    
         decnum += int(digit, 16) * (16 ** power)       
     print(decnum)
 
hex_output()

reversed returns a new iterable, which returns another iterable’s elements in reverse order. By invoking enumerate on the output from reversed, we get each element of hexnum, one at a time, along with its index, starting with 0.

Python’s ** operator is used for exponentiation.

You can work through this code in the Python Tutor at http://mng.bz/Qy8e.

Screencast solution

Watch this short video walkthrough of the solution: https://livebook.manning.com/ video/python-workout.

Beyond the exercise

Every Python developer should have a good understanding of the iterator protocol, which for loops and many functions use. Combining for loops with other objects, such as enumerate and slices, can help to make your code shorter and more maintainable.

  • Reimplement the solution for this exercise such that it doesn’t use the int function at all, but rather uses the built-in ord and chr functions to identify the character. This implementation should be more robust, ignoring characters that aren’t legal for the entered number base.

  • Write a program that asks the user for their name and then produces a “name triangle”: the first letter of their name, then the first two letters, then the first three, and so forth, until the entire name is written on the final line.

Summary

It’s hard to imagine a Python program that doesn’t use numbers. Whether as numeric indexes (into a string, list, or tuple), counting the number of times an IP address appears in a log file, or calculating interest rates on bank loans, you’ll be using numbers all of the time.

Remember that Python is strongly typed, meaning that integers and strings (for example) are different types. You can turn strings into integers with int, and integers into strings with str. And you can turn either of these types into a floating-point number with float.

In this chapter, we saw a few ways we can work with numbers of different types. You’re unlikely to write programs that only use numbers in this way, but feeling confident about how they work and fit into the larger Python ecosystem is important.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset