Categories
Python

Organize Code with Python Functions

Python is a convenient language that’s often used for scripting, data science, and web development.

In this article, we’ll look at how to define and use Python functions.

Functions

A function is a piece of code that we can invoke repeatedly in different places.

It’s defined with the def keyword as follows:

def greet(name):  
  print('Hello', name)

The greet function above has one parameter. We can pass in an argument to set the value of the parameter when we call it.

Functions always start with the def keyword, then the function name, then parentheses and zero or more parameters inside. Then the first line ends with a colon.

The code for the function is indented inside the function block.

For example, we can call greet as follows:

greet('Joe')

Then we see:

Hello Joe

displayed on the screen since we passed in 'Joe' as the argument of the greet function call.

Functions can call other functions. For example, we can write the following code to make our greet function call another function:

def greet(first_name, last_name):  
  print('Hello', full_name(first_name, last_name))

def full_name(first_name, last_name):  
  return '%s %s' % (first_name, last_name)

greet('Jane', 'Smith')

In the code above, our greet function calls the full_name function which returns the full name constructed by combining first_name and last_name .

In the full_name function, we use the return keyword to return the computed result of combining the first_name and last_name parameters into one string.

The function ends whenever we use the return keyword. Nothing below it will run.

Therefore, we can use it as follows to return the values that we want to return conditionally by using multiple return statements as follows:

import random

def crystal_ball(num):  
  if num == 1:  
    return 'It is a great day'  
  elif num == 2:  
    return 'It is a lucky day'  
  elif num == 3:  
    return 'It is an auspicious day'

r = random.randint(1, 4)  
fortune = crystal_ball(r)  
print(fortune)

In the code above, we have if statements to return something whenever the value of num is 1, 2, or 3.

If num is 1, then crystal_ball returns 'It is a great day' .

If num is 2, then crystal_ball returns 'It is a lucky day' .

If num is 3, then crystal_ball returns ‘It is an auspicious day’ .

Once something is returned, crystal_ball function stops running.

We can then assigned the returned value to another variable for storage as we did by writing:

fortune = crystal_ball(r)

Then we printed out the value of fortune .

The None Value

In Python, we have the value None to represent no value. None has the type NoneType .

None has the capital N .

We can use it as a return value of something that shouldn’t have a value.

For example, the print returns None since there’s nothing to return. It just prints a value on the screen.

If we write:

foo = print('Hello!')  
print(None == foo)

In the code above, we should see:

Hello!  
True

printed on the screen since print returns None , so the value that’s assigned to foo would be None .

Therefore, None == foo returns True .

Keyword Arguments

Python functions can have named arguments. This way, we know what values the arguments are set when we’re passing them.

For example, we can pass in named parameters as follows:

def full_name(first_name, last_name):
  return '%s %s' % (first_name, last_name)

print(full_name(first_name='Jane', last_name='Smith'))

In the code, we called full_name by writing:

full_name(first_name='Jane', la_name='Smith')

Now we know that we’re passing 'Jane' as the value of the first_name parameter and 'Smith' as the value of the last_name parameter.

The Call Stack

The call stack is a data structure that tells us what function we’ve called in the order they’re called.

The earliest called function is at the bottom of the stack, and the later ones are higher in the stack.

For example, in the example we have earlier:

def greet(first_name, last_name):
  print('Hello', full_name(first_name, last_name))

def full_name(first_name, last_name):
  return '%s %s' % (first_name, last_name)

greet('Jane', 'Smith')

Our call stack would have the greet function at the bottom and full_name at the top.

Local and Global Scope

Variables that are inside a function has a local scope. It’s only available inside the function and nested function inside it.

They can’t be referenced outside the function.

Anything at the top level of a Python file has global scope.

Variables with global scope can be accessed inside anything located in a lower function like a block or function.

For example, if we have:

x = 1

def show_x():
  print(x)

show_x()

Then we can reference x inside the show_x function as x has global scope.

On the other hand, if we have:

def show_x():  
  x = 1

print(x)

We’ll get an error saying x isn’t defined.

Having scopes lets us narrow down the scope of the code that can cause a bug. We don’t have to search through the whole program to troubleshoot everything.

Conclusion

Functions are used to organize code into reusable pieces. They take parameters and we call them by passing in arguments.

They can return something with the return keyword. Returned values from a function can be assigned to other variables.

Variables inside functions have a local scope. They can’t be accessed from outside the function.

On the other hand, variables at the top level have global scope, which can be accessed inside functions.

Categories
Python

Quick Intro to Python Modules

Python is a convenient language that’s often used for scripting, data science, and web development.

In this article, we’ll look at how to define and use modules to separate large programs into small pieces.

Importing Modules

Most programs are more than a few lines long. This means that they have to split into multiple small pieces for them to be manageable.

Python programs can be split into modules. Each module is a Python program. For example, the math module has math functions and the random module has random number-related functions.

To use Python modules in another module, we have to use the import keyword and the name of the module.

If we want to import more than one module, we can separate the module names by commas.

For example, we can import the random module and then print out a randomly generated number by calling the randrange function as follows:

import random  
print(random.randrange(0, 101, 2))

In the code above, we import the random module and called the randrange function with the start and end numbers of the number we want to generate as the first 2 arguments.

The end number is excluded from the range of numbers that can be generated.

The last argument has the number of steps to skip between the start and end numbers.

The code above will generate an even number in the range of 0 to 100 inclusive.

from import Statements

We can just import one member of a Python module with the from keyword.

For example, we can just import the randrange function from the random module as follows:

from random import randrange  
print(randrange(0, 101, 2))

In the code above, we just import the randrange function instead of the whole module.

But the rest of the logic is the same.

This is more efficient since we didn’t import everything.

Create Our Own Modules

Anything that is at the top level of a Python file can be imported from a Python module.

For example, we can create a module called foo.py as follows:

x = 1

Then in main.py , we can import and use it as follows:

import foo  
print(foo.x)

We should see 1 on the screen since we have x set to 1 and imported it.

The Module Search Path

Python modules are searched in the code directory, the path set as the value of the PYTHONPATH environment variable, and the default directory that’s set when Python is installed.

The dir() Function

The dir function is used to list the members of a Python module.

For example, we can print a list of members of the math module as follows:

import math  
print(dir(math))

Then we get the following on the screen:

['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'pi', 'pow', 'radians', 'remainder', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc']

We can see the functions that we can call from the array above.

Packages

We can put Python files in directories to organize them into packages.

For example, we can put foo.py in the package folder, add __init__.py to it.

Then we can import and use package’s members as follows:

from package import foo  
print(foo.x)

Importing * From a Package

The asterisk character indicates that we import all the members from a package.

For example, if we write:

from sound.effects import *

we import all the members from the effects module in the sound package.

This is bad practice because the code is inefficient because we import everything. Also, we may have clashing names since we import more members than we’re supposed to.

Import as

We can use the as keyword to import a module with an alias name. This helps us avoid clashes of names from different modules where we have members that have the same name in different modules.

For example, we can write the following code:

import random as r  
print(r.randrange(0, 101, 2))

to import the random module with an alias r and reference that instead of random .

We can also import a member with as an alias as follows:

from random import randrange as rr  
print(rr(0, 101, 2))

We can call rr instead of randrange to call randrange .

Conclusion

We can define and import Python modules by creating a Python code file and then we can import the members of the Python file.

This lets us divide code into small pieces.

Also, we can organize Python modules into packages by putting module files in folders and add __init__.py to each directory.