Understanding Python Functions and Lambda Expressions

Consider creating a function to calculate the area of a trapezoid, then use it to determine the area of a trapezoid with an upper base of 4cm, lower base of 3cm, and height of 5cm. However, swapping the positions of the height and lower base parameters would yield incorrect results:

def area(upper_base, lower_base, height):
    return (upper_base + lower_base) * height / 2

print("Correct result:", area(4, 3, 5))
print("Incorrect result:", area(4, 5, 3))

The output will be:

Correct result: 17.5
Incorrect result: 13.5

Thus, parameter ordering is crucial when calling functions to avoid such errors that might be difficult to spot.

Keyword Arguments in Python Functions

Until now, we've used positional arguments where actual parameters must correspond exactly to formal parameters in number and order. This section introduces keyword arguments which allow more flexible parameter passing.

Keyword arguments specify parameter values by their names rather than their positions. When using this approach, the order of parameters doesn't matter as long as the names match correctly.

Therefore, function parameter names should be semantically meaningful to clearly indicate what each passed parameter represents.

Here's an example using keyword arguments:

def display_strings(str1, str2):
    print("str1:", str1)
    print("str2:", str2)

# Using positional arguments
display_strings("http://c.biancheng.net/python/", "http://c.biancheng.net/shell/")

# Using keyword arguments
display_strings("http://c.biancheng.net/python/", str2="http://c.biancheng.net/shell/")
display_strings(str2="http://c.biancheng.net/python/", str1="http://c.biancheng.net/shell/")

The execution results show:

str1: http://c.biancheng.net/python/
str2: http://c.biancheng.net/shell/
str1: http://c.biancheng.net/python/
str2: http://c.biancheng.net/shell/
str1: http://c.biancheng.net/shell/
str2: http://c.biancheng.net/python/

It's evident that both positional and keyword arguments can be used when calling parameterized functions. In keyword argument usage, parameter order is arbitrary. Mixed usage is also allowed, but all keyword arguments must follow all positional arguments. For example, the following code is invalid:

# Positional arguments must precede keyword arguments
display_strings(str1="http://c.biancheng.net/python/", "http://c.biancheng.net/shell/")

Python will throw a syntax error:

SyntaxError: positional argument follows keyword argument

Default Parameters in Python Functions

When calling a function without specifying certain parameters, Python raises an exception. To address this, Python allows setting default values for parameters during function definition. If a parameter has a default value and isn't explicitly provided during the call, it uses its default value.

The syntax for defining a function with default parameters is:

def function_name(..., parameter_name, parameter_name=default_value):
    # body

Note that parameters with defaults must come after those without defaults to prevent syntax errors.

The following example demonstrates defining and calling functions with default parameters:

# str1 has no default, str2 does
def display_strings(str1, str2="http://c.biancheng.net/python/"):
    print("str1:", str1)
    print("str2:", str2)

display_strings("http://c.biancheng.net/shell/")
display_strings("http://c.biancheng.net/java/", "http://c.biancheng.net/golang/")

Output:

str1: http://c.biancheng.net/shell/
str2: http://c.biancheng.net/python/
str1: http://c.biancheng.net/java/
str2: http://c.biancheng.net/golang/

In this case, the second parameter has a default value. When calling the function with just one argument, it assigns to str1, while str2 takes its default value. Alternatively, both arguments can be specified, overriding the default value of str2.

Combining with keyword arguments, the following calls are valid:

display_strings(str1="http://c.biancheng.net/shell/")
display_strings("http://c.biancheng.net/java/", str2="http://c.biancheng.net/golang/")
display_strings(str1="http://c.biancheng.net/java/", str2="http://c.biancheng.net/golang/")

Remember, default parameters must appear after non-default ones. The following definition is incorrect:

# Syntax error
def display_strings(str1="http://c.biancheng.net/python/", str2, str3):
    pass

Here, str1 has a default value, while str2 and str3 do not, so str1 must be placed after them.

To check default values of parameters in built-in or third-party functions, use the __defaults__ attribute:

print(display_strings.__defaults__)

This outputs:

('http://c.biancheng.net/python/',)

Variable Arguments (*args, **kwargs)

Python supports variable-length arguments allowing functions to accept any number of parameters. These are known as variable arguments or indefinite arguments.

1) Variable Arguments with *

The syntax is:

*args

This creates an empty tuple named args that accepts any number of non-keyword arguments.

An example:

# Function accepting variable arguments
def display_strings(home, *strings):
    print(home)
    print("strings =", strings)
    for s in strings:
        print(s)

# Passing any number of arguments
display_strings("http://c.biancheng.net",
                "http://c.biancheng.net/python/",
                "http://c.biancheng.net/shell/")

Output:

http://c.biancheng.net
strings = ('http://c.biancheng.net/python/', 'http://c.biancheng.net/shell/')
http://c.biancheng.net/python/
http://c.biancheng.net/shell/

Here, the last parameter collects all extra non-keyword arguments into the strings tuple.

Variable arguments don't have to be at the end. Consider modifying the function:

# Function accepting variable arguments
def display_strings(*strings, home):
    print(home)
    print("strings =", strings)
    for s in strings:
        print(s)

display_strings("http://c.biancheng.net",
                "http://c.biancheng.net/python/",
                home="http://c.biancheng.net/shell/")

In this case, strings is the first parameter. However, when calling the function, the regular parameter must be passed as a keyword argument, otherwise all arguments are assigned to strings.

2) Variable Arguments with **

The syntax is:

**kwargs

This creates a dictionary named kwargs that receives any number of keyword arguments.

Example:

# Function accepting variable arguments
def display_strings(home, *strings, **courses):
    print(home)
    print(strings)
    print(courses)

# Calling function
display_strings("C语言中文网",
                "http://c.biancheng.net",
                "http://c.biancheng.net/python/",
                shell_course="http://c.biancheng.net/shell/",
                go_course="http://c.biancheng.net/golang/",
                java_course="http://c.biancheng.net/java/")

Output:

C语言中文网
('http://c.biancheng.net', 'http://c.biancheng.net/python/')
{'shell_course': 'http://c.biancheng.net/shell/', 'go_course': 'http://c.biancheng.net/golang/', 'java_course': 'http://c.biancheng.net/java/'}

Here, the first argument goes to home, the next two non-keyword arguments to strings, and the last three keyword arguments to courses.

By default, *args is an empty tuple, and **kwargs is an empty dictionary. Functions with these arguments can be called without providing values.

Reverse Parameter Collection

Python allows unpacking lists, tuples, and dictionaries directly into function arguments. When passing a list or tuple, prefix with *, and when passing a dictionary, prefix with **.

Example:

def display_strings(name, address):
    print("name:", name)
    print("address:", address)

data = ["Python tutorial", "http://c.biancheng.net/python/"]
# Using reverse parameter collection
display_strings(*data)

Output:

name: Python tutorial
address: http://c.biancheng.net/python/

Another example with a dictionary:

def display_strings(name, address):
    print("name:", name)
    print("address:", address)

data = {'name': "Python tutorial", 'address': "http://c.biancheng.net/python/"}
# Using reverse parameter collection
display_strings(**data)

Output:

name: Python tutorial
address: http://c.biancheng.net/python/

Reverse parameter collection works with functions that have variable arguments:

def display_strings(name, *addresses):
    print("name:", name)
    print("addresses:", addresses)

data = ["http://c.biancheng.net/python/",
        "http://c.biancheng.net/shell/",
        "http://c.biancheng.net/golang/"]

# Using reverse parameter collection
display_strings("Python tutorial", *data)

Output:

name: Python tutorial
addresses: ('http://c.biancheng.net/python/', 'http://c.biancheng.net/shell/', 'http://c.biancheng.net/golang/')

If not prefixed with *, Python treats the entire list as a single argument:

def display_strings(name, *addresses):
    print("name:", name)
    print("addresses:", addresses)

data = ["Python tutorial",
        "http://c.biancheng.net/python/",
        "http://c.biancheng.net/shell/",
        "http://c.biancheng.net/golang/"]

display_strings(data)  # Passes the whole list as one argument

Output:

name: ['Python tutorial', 'http://c.biancheng.net/python/', 'http://c.biancheng.net/shell/', 'http://c.biancheng.net/golang/']
addresses: ()

Python None Type

None is a special constant in Python representing the absence of a value. Unlike False, it does not represent zero or an empty string. It belongs to the NoneType class.

None is distinct from empty containers like [] and "":

None is []  # False
None is ""  # False
type(None)  # <class>
</class>

None is used in assertions, conditional checks, and when functions don't return anything. For instance, print() returns None:

spam = print('Hello!')
# Hello!
None == spam  # True

All functions without explicit return statements implicitly return None.

Return Statement in Functions

Functions can return values using the return statement. A function may contain multiple return statements, but only one executes per function call.

Syntax:

return [value]

Example:

def add(a, b):
    c = a + b
    return c

# Assigning function result to variable
result = add(3, 4)
print(result)  # 7

# Using function result as argument
print(add(3, 4))  # 7

Functions can also return boolean values:

def is_greater_zero(x):
    if x > 0:
        return True
    else:
        return False

print(is_greater_zero(5))  # True
print(is_greater_zero(0))  # False

Returning Multiple Values

While a function typically returns one value, it can effectively return multiple values through sequences like lists or tuples.

Two approaches:

  1. Create a list or tuple containing values to return
  2. Return multiple values separated by commas; Python wraps them in a tuple

Example:

def return_list():
    urls = [
        "http://c.biancheng.net/python/",
        "http://c.biancheng.net/shell/",
        "http://c.biancheng.net/golang/"
    ]
    return urls

def return_tuple():
    return (
        "http://c.biancheng.net/python/",
        "http://c.biancheng.net/golang/",
        "http://c.biancheng.net/golang/"
    )

print("return_list =", return_list())
print("return_tuple =", return_tuple())

Output:

return_list = ['http://c.biancheng.net/python/', 'http://c.biancheng.net/shell/', 'http://c.biancheng.net/golang/']
return_tuple = ('http://c.biancheng.net/python/', 'http://c.biancheng.net/golang/', 'http://c.biancheng.net/golang/')

Use sequence unpacking to receive multiple values:

def return_list():
    urls = [
        "http://c.biancheng.net/python/",
        "http://c.biancheng.net/shell/",
        "http://c.biancheng.net/golang/"
    ]
    return urls

python_url, shell_url, golang_url = return_list()
print("python_url =", python_url)
print("shell_url =", shell_url)
print("golang_url =", golang_url)

Output:

python_url = http://c.biancheng.net/python/
shell_url = http://c.biancheng.net/shell/
golang_url = http://c.biancheng.net/golang/

Recursive Functions

A recursive function calls itself within its body. Each call enters a new layer until the innermost completes, then unwinds.

Example: Given a sequence where f(0)=1, f(1)=4, f(n+2)=2*f(n+1)+f(n), find f(10).

def fn(n):
    if n == 0:
        return 1
    elif n == 1:
        return 4
    else:
        return 2 * fn(n - 1) + fn(n - 2)

print("fn(10) result:", fn(10))

The recursion ends when it reaches base cases (n=0 or n=1). Recursive functions must progress toward known values to avoid infinite recursion.

Variable Scope (Local vs Global)

Variable scope defines where a variable can be accessed. Variables defined inside a function are local, while those outside are global.

Local Variables

Variables defined within a function exist only within that scope:

def demo():
    address = "http://c.biancheng.net/python/"
    print("Inside function:", address)

demo()
print("Outside function:", address)  # Error: NameError

Function parameters are also local variables.

Global Variables

Variables defined outside all functions are global and accessible throughout the program:

address = "http://c.biancheng.net/shell/"

def text():
    print("Inside function:", address)

text()
print("Outside function:", address)

To modify a global variable inside a function, use the global keyword:

def text():
    global address
    address = "http://c.biancheng.net/java/"
    print("Inside function:", address)

text()
print("Outside function:", address)

Accessing Variables by Scope

Python provides functions to access variables by scope:

  • globals(): Returns a dictionary of all global variables
  • locals(): Returns a dictionary of all variables in current scope
  • vars(object): Returns a dictionary of variables in a specified object's scope

Using Global Variables with Same Names

If a local variable shares the same name as a global one, the local version shadows the global:

name = "Python tutorial"

def demo():
    print(name)  # Accesses global variable
    name = "Shell tutorial"  # Creates local variable

demo()  # Error: UnboundLocalError

To access or modify the global variable:

  1. Use globals():
name = "Python tutorial"

def demo():
    print(globals()['name'])  # Access global
    globals()['name'] = "Java tutorial"
    name = "Shell tutorial"  # Local variable

demo()
print(name)

  1. Use the global keyword:
name = "Python tutorial"

def demo():
    global name
    print(name)  # Access global
    name = "Shell tutorial"  # Modify global

demo()
print(name)

Nested Functions and the nonlocal Keyword

Functions can be defined inside other functions. Nested functions can only be accessed within their parent function unless returned.

def outer_function():
    def inner_function():
        print("http://c.biancheng.net/python/")
    inner_function()

outer_function()

To extend the scope of a nested function:

def outer_function():
    def inner_function():
        print("Calling inner function")
    return inner_function

new_inner = outer_function()
new_inner()  # Works

If a nested function defines a variable with the same name as an outer function's variable, it shadows it:

def outer_function():
    name = "Variable defined in outer function"
    
    def inner_function():
        print(name)
        name = "Variable defined in inner function"  # Error: UnboundLocalError
    
    inner_function()

outer_function()

Use the nonlocal keyword to access the outer variable:

def outer_function():
    name = "Variable defined in outer function"
    
    def inner_function():
        nonlocal name
        print(name)
        name = "Variable defined in inner function"
    
    inner_function()

outer_function()

Lambda Expressions (Anonymous Functions)

For simple functions with a single expression, Python offers lambda expressions:

name = lambda [parameters] : expression

Equivalent to:

def name(parameters):
    return expression

Example:

add = lambda x, y: x + y
print(add(3, 4))  # 7

Lambda expressions provide concise syntax for one-line functions, improving code readability and performance for temporary use.

Functional Programming with map(), filter(), and reduce()

Functional programming emphasizes pure functions and immutability. Python supports functional programming through map(), filter(), and reduce().

map() Function

Applies a function to each element of an iterable:

numbers = [1, 2, 3, 4, 5]
doubled = map(lambda x: x * 2, numbers)
print(list(doubled))  # [2, 4, 6, 8, 10]

filter() Function

Returns elements for which a predicate function evaluates to True:

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = filter(lambda x: x % 2 == 0, numbers)
print(list(evens))  # [2, 4, 6, 8, 10]

reduce() Function

Applies a binary function cumulatively to the items of an iterable:

from functools import reduce
numbers = [1, 2, 3, 4, 5]
product = reduce(lambda x, y: x * y, numbers)
print(product)  # 120

Tags: python function lambda Recursion scope

Posted on Tue, 26 May 2026 20:25:09 +0000 by MattMan