Book Image

Daniel Arbuckle's Mastering Python

By : Daniel Arbuckle
Book Image

Daniel Arbuckle's Mastering Python

By: Daniel Arbuckle

Overview of this book

Daniel Arbuckle's Mastering Python covers the basics of operating in a Python development environment, before moving on to more advanced topics. Daniel presents you with real-world solutions to Python 3.6 and advanced-level concepts, such as reactive programming, microservices, ctypes, and Cython tools. You don't need to be familiar with the Python language to use this book, as Daniel starts with a Python primer. Throughout, Daniel highlights the major aspects of managing your Python development environment, shows you how to handle parallel computation, and helps you to master asynchronous I/O with Python 3.6 to improve performance. Finally, Daniel will teach you the secrets of metaprogramming and unit testing in Python, helping you acquire the perfect skillset to be a Python expert. Daniel will get you up to speed on everything from basic programming practices to high-end tools and techniques, things that will help set you apart as a successful Python programmer.
Table of Contents (13 chapters)

Python basic syntax and block structure

This section primarily provides a basic understanding of the Python language constructs. If you feel you already have a solid grasp of Python, feel free to skip ahead.

Let's get down to the nuts and bolts.

A Python program is written as source code in one or more .py files and consists of statements and expressions as shown in the following screenshot:

Both statements and expressions tell Python to do something. The difference is that expressions can be combined to form more complex expressions, while statements can be combined with expressions, but not with other statements.

For example, a statement looks like this:

if 2 > 1: 

An expression looks like this:

print ("One is the loneliest number") 

Python source code files are executed from top to bottom as soon as they're loaded by the Python runtime. This means that for simple programs, we could just write a series of statements in a .py file and then tell Python to run them. In the preceding example, the if and else parts are statements or a single statement with two parts, if you prefer to think of it that way. Everything else is an expression. For more complex programs, we need a more structured approach.

Like most programming languages, Python lets us create functions and classes in order to organize our code.

If you don't know what functions or classes are, you could think of functions as miniature programs that can be used as building blocks for larger programs and classes as combinations of functions and data to create new kinds of data.

Basic building blocks

To organize our code, we can divide it into four basic building blocks. We'll discuss each of these separately for understanding their role and importance in the Python code. These are as follows:

  • Functions
  • Variables
  • Expressions
  • Classes

Functions

We'll start with a brief look at functions. Functions are created using a def statement, which is a statement using the def keyword as its identifying component. As I said earlier, Python executes the statements in a .py file, starting from the top, as shown in the following screenshot:

When Python executes a def statement, it creates a function as a result. This means that the code that runs before the def statement does not see the function because it doesn't exist yet. The part of the def line inside parentheses is called the parameter list:

example_function(name, radius): 

The parameter list is a list of internal names for data values that are given to the function as the input. Outside the function, these values might have different names or no names at all, but inside, they'll be stored in these variables.

The indented block of code immediately after the def line is called the function body, and you could think of it as the source code of the function:

def example_function(name, radius): 
    area = math.pi * radius ** 2 
    return "The area of {} is {}" .format(name, area) 

The following screenshot shows the output of the preceding example:

The code inside the function body is an exception to the rule about running Python code from the top to the bottom of the file. This code is stored away and then executed later, when we tell the function to run.

Like the code in a file, the code in a function runs from top to bottom, one statement or expression at a time.

If you're more familiar with C++ or Java, you may be wondering where the function parameter types and return types are. In Python, the data type is inherent in each data value, so the runtime always knows what type of data we're working with and whether what we're trying to do is a valid operation for that data type. Thus, for the most part, we don't need explicit data types.

Python programmers sometimes talk about duck typing, which is a reference to the following saying:

If it quacks like a duck, it's probably a duck.

What they mean by this saying is that if the operations we're trying to perform on a data value work, it doesn't really matter if it's precisely the kind of data we expected. It's probably close enough. If they don't work, Python will tell us what went wrong and where, which is often more useful to know than the kind of information that can be determined by comparing data types alone.

For situations where we want or need to specify data types, we can use function annotations and the standard library typing module.

Function decorators, which we'll discuss in later chapters, can provide a convenient way of enforcing these annotations.

Variables

The second major building block of a Python program is called a variable. A variable is pretty much just a box for storing a data value. The variable has a name and we can use that name to access the data stored in the variable or to replace the data with a new value.

The function parameters in the previous examples were variables, as was area:

(name, radius):  

To set the data stored in a variable, we use an assignment statement. An assignment is a statement, so remember this means that it can't be combined with any other statement. It gets a line of source code all for itself and the expressions that are part of it.

An assignment statement consists of the variable's name on the left-hand side of an equal to symbol and the value we want to store in the variable on the right-hand side, as shown in the following code:

outer = "Hello world"  

If the variable didn't already exist, it will be created. Irrespective of whether the variable existed before or not, the value is stored in the variable.

Variables that are created inside a function are only visible inside that function and each time the function runs they're created a new.

The following code provides an example of this in action:

outer = "Hello world"  
def example_function(param): 
    inner = "Hello function: {}".format(param) 
    print(inner, outer) 
example_function("first") 
example_function("second") 
print(inner) 

The last line of the preceding example demonstrates that the variable created inside the function does not exist for code outside the function, as shown in the following output of the code:

This code example also shows what happens when we try to ask Python to do something impossible. It tells us what we did wrong and gives us the information about where the problem occurred and how we got there.

Expressions

The third major building block of Python programs is expressions. We've seen expressions in every example so far because it's nearly impossible to do anything in Python without using expressions.

Expressions consist of data values and operations to perform on those data values. The very simple expressions are a single data value and with no operations, for example, a single number. More complex expressions involve at least one operation and probably more data values as well, for example, adding two numbers or calculating the area, as shown in the following code example:

import math 
 
def example_function(name: str, radius: float) -> str: 
  area = math.pi * radius ** 2 
  return "The area of {} is {}" .format(name, area)  
 
print(example_function('Bob', 5)) 

All expressions produce a resulting data value of some sort; for example, adding two numbers produces the sum as another number, while concatenating two text strings produces the concatenation as another text string. Using a name variable to look up the stored value is an expression, so is running a function.

If the function doesn't explicitly return a value, the result is a special value called none.

Anywhere we need a value, we can use any expression that produces the needed value. It doesn't matter whether the expression is a simple number, such as 55, a variable name, a complex combination of values and operators, a function call, or any other expression. At least, it doesn't matter as far as the final result is concerned. Some expressions take less time to execute than others, so speed can be a factor.

Classes

The final fundamental building block we're going to discuss in this section is classes. The word class is a synonym for category or type; in this case, it is referring to data values.

A class defines a new kind of data value by describing a set of internal data and operations for that type of data value. This is done primarily by defining a group of functions that make up the class. A special function called __init__ is used to set up the internal data for a new data value of this type, and the rest of the functions define the operations on an existing data value of this type:

class Frood: 
    def __init__(self, age): 
        self.age = age 
        print("Frood initialized") 
 
    def anniversary(self): 
        self.age += 1 
        print("Frood is now {} years old".format(self.age)) 
 
f1 = Frood(12) 
f2 = Frood(97) 
f1.anniversary() 
f2.anniversary() 
f1.anniversary() 
f2.anniversary() 

All the functions of a class receive a parameter called self, as shown in the preceding code example for classes. This parameter is the data value being operated on. That's different from C++ or Java because while those languages do basically the same thing, the parameter is implicit instead of being an explicit part of the function's parameter list.

Class functions, including __init__, should store and retrieve data from self when they want to manipulate the data value that they're connected to.

Classes support inheritance and multiple inheritance, but we won't go into that in detail at this point in the book.

In the preceding example, we created a new data type called Frood and then made two separate data values of that type. Then, we used the anniversary function that we created as part of the class to modify each of them.

The output of the code example for classes is as follows:

The two instances maintain their internal variables with different values, as shown in the preceding output.

Flow control statements

Python has several flow control statements that will be familiar to people who know another language in the C family. For example, Python has loops and if, elif, and else branches (shown in the following code example):

selector = 5 
 
if selector < 3: 
    print("less than three") 
elif selector < 6: 
    print("less than six") 
else: 
    print("six or more") 
while selector > 0" 
    print('selector is {}' .format(selector)) 
    selector -=1 
 
for x in ['a', 'b', 'c', 'd']: 
    print(x) 
for x in range(5): 
    print(x) 

Python also has a for loop statement, but it's not like the for loops in C, C++, or Java. Instead of counting through numbers, the for loop iterates through the values. If we actually want to count through numbers with a for loop, that's easily done using a range iterator, as shown in the following screenshot in the output of the preceding code example:

Before we wrap-up this section, there's one last thing I should comment on and that's Python's views on indentation to signify the block structure.

Indentation

Most other programming languages have explicit symbols that indicate the beginning of a block and the end of a block. However, it's a common practice in all of those languages to indent blocks so that humans find the code easier to read. In fact, failure to do so is often taken as a sign that a programmer is an amateur. This means that the block structure in most languages is actually represented in two different ways: the symbols and the indentation. By incorporating indentation into syntax without the need for explicit symbols, Python both removes this duplication and ensures that the code is readable.

With that, we've come to the end of this section. In the next section, we'll look at some of Python's built-in data structures and the data processing syntax.