Chapter 8: Functions

Functions are named blocks of code that do one specific job. Write once, call anywhere.

Defining a Function

def greet():
    """Display a simple greeting."""  (1)
    print("Hello!")

greet()  # Call the function
1 Docstring - describes what the function does

Passing Arguments

Parameters vs Arguments

  • Parameter: Variable in function definition

  • Argument: Value passed when calling

def ping(host):        # host is parameter
    print(f"Pinging {host}...")

ping('web-01')         # 'web-01' is argument

Positional Arguments

Order matters:

def connect(host, port):
    print(f"Connecting to {host}:{port}")

connect('10.0.1.10', 443)       # host='10.0.1.10', port=443
connect(443, '10.0.1.10')       # Wrong order - host=443

Keyword Arguments

Explicit naming, order doesn’t matter:

connect(host='10.0.1.10', port=443)
connect(port=443, host='10.0.1.10')  # Same result

Default Values

def connect(host, port=22):  (1)
    print(f"Connecting to {host}:{port}")

connect('web-01')           # Uses default port 22
connect('web-01', 443)      # Overrides to 443
1 Parameters with defaults must come after those without

Argument Errors

def connect(host, port):
    print(f"{host}:{port}")

connect('web-01')  # TypeError: missing required argument 'port'

Python’s error tells you exactly what’s missing.

Return Values

Functions can process data and return results:

def get_fqdn(hostname, domain='example.com'):
    """Return fully qualified domain name."""
    return f"{hostname}.{domain}"

fqdn = get_fqdn('web-01')
print(fqdn)  # web-01.example.com

Optional Arguments

Use empty string or None as default for optional args:

def format_address(host, port, protocol=''):
    """Format connection string."""
    if protocol:
        return f"{protocol}://{host}:{port}"
    return f"{host}:{port}"

format_address('web-01', 443)             # 'web-01:443'
format_address('web-01', 443, 'https')    # 'https://web-01:443'

Returning Dictionaries

def build_server(hostname, ip, role='web'):
    """Build a server info dictionary."""
    return {
        'hostname': hostname,
        'ip': ip,
        'role': role
    }

server = build_server('web-01', '10.0.1.10')

Returning None

Functions without explicit return (or bare return) return None:

def log_message(msg):
    print(f"[LOG] {msg}")
    # no return statement

result = log_message("test")
print(result)  # None

Passing Lists

Lists passed to functions can be modified:

def deploy_servers(pending, completed):
    """Deploy servers from pending to completed list."""
    while pending:
        server = pending.pop()
        print(f"Deploying {server}...")
        completed.append(server)

to_deploy = ['web-01', 'web-02', 'db-01']
deployed = []
deploy_servers(to_deploy, completed)

print(to_deploy)   # [] - empty, was modified
print(deployed)    # ['db-01', 'web-02', 'web-01']

Preventing Modification

Pass a copy using slice:

deploy_servers(to_deploy[:], deployed)  # [:] creates copy
# to_deploy unchanged

Only do this when you need the original preserved.

Arbitrary Arguments

*args - Variable Positional

*args collects extra positional arguments into a tuple:

def add_ports(*ports):
    """Add ports to firewall."""
    for port in ports:
        print(f"Opening port {port}")

add_ports(22)
add_ports(80, 443, 8080)

Mix with regular parameters (regular first):

def configure_service(name, *ports):
    print(f"Service: {name}")
    for port in ports:
        print(f"  Port: {port}")

configure_service('nginx', 80, 443)

**kwargs - Variable Keyword

**kwargs collects extra keyword arguments into a dictionary:

def build_config(hostname, **settings):
    """Build configuration with arbitrary settings."""
    config = {'hostname': hostname}
    config.update(settings)
    return config

config = build_config('web-01',
                       ip='10.0.1.10',
                       port=443,
                       ssl=True)
# {'hostname': 'web-01', 'ip': '10.0.1.10', 'port': 443, 'ssl': True}

Modules

Store functions in separate files for reuse.

Importing a Module

# network.py
def ping(host):
    print(f"Pinging {host}...")

def scan(host, port):
    print(f"Scanning {host}:{port}")
# main.py
import network

network.ping('web-01')
network.scan('web-01', 443)

Import Specific Functions

from network import ping, scan

ping('web-01')  # No module prefix needed

Aliases

# Module alias
import network as net
net.ping('web-01')

# Function alias
from network import scan as port_scan
port_scan('web-01', 443)

Import All (Avoid)

from network import *  # Imports everything - can cause name conflicts

Use explicit imports or module prefix instead.

Styling Functions

def function_name(param1, param2='default'):  (1)
    """Docstring describing function."""       (2)
    # function body
    return result
1 No spaces around = in default values
2 Docstring immediately after definition

Long parameter lists:

def configure_server(
        hostname, ip, port,
        ssl=True, timeout=30):
    """Configure server with given parameters."""
    # ...

Two blank lines between functions in a module.

Quick Reference

Pattern Code

Define

def name(params):

Docstring

"""Description."""

Default value

def f(x, y=10):

Keyword arg

f(x=1, y=2)

Return value

return result

Variable positional

def f(*args):

Variable keyword

def f(**kwargs):

Import module

import module

Import function

from module import func

Alias

import module as m

Exercises

8-1. Message

Write display_message() that prints what you’re learning. Call it.

8-2. Servers

Write describe_server(hostname, ip). Call with positional and keyword args.

8-3. Service Config

Write configure_service(name, port=80) with default port. Call both ways.

8-4. Connection String

Write function returning "protocol://host:port". Make protocol optional.

8-5. Server Builder

Write function accepting hostname plus arbitrary **kwargs, returning dict.

8-6. Network Module

Put 3 network-related functions in network.py. Import and use each way.

Summary

  • Functions: def name(params): with docstring

  • Positional arguments must match order

  • Keyword arguments are explicit: name=value

  • Default values make parameters optional

  • args collects variable positional, *kwargs collects keyword

  • return sends data back to caller

  • Modules organize functions in separate files

Next: Classes for object-oriented programming.