Task06: Functions and Lambda Expressions

Functions and Lambda Expressions

1. Function

Remember "everything is an object" in Python? Python also treats functions as objects, which can be returned from another function to build higher-order functions, such as:

  • parameters are functions
  • The return value is a function

function definition

  • A function begins with the def keyword, followed by the function name and parentheses ().
  • The code executed by the function starts with a colon and is indented.
  • return [expression] Ends a function, optionally returning a value to the caller. return without an expression is equivalent to returning None.
def functionname(parameters):
    "function_docstring"
    function_suite
    return [expression]

function call

[example]

def printme(str):
    print(str)


printme("I want to call a user-defined function!")  # I want to call a user-defined function!
printme("call the same function again")  # call the same function again
temp = printme('hello') # hello
print(temp)  # None

function documentation

def MyFirstFunction(name):
    "During function definition name is a formal parameter"
    # Because Ta is just a form, indicating that it occupies a parameter position
    print('passed in{0}is called an argument, because Ta is the specific parameter value!'.format(name))


MyFirstFunction('The program life of the old horse')  
# The program life of the old horse passed in is called an actual parameter, because Ta is a specific parameter value!

print(MyFirstFunction.__doc__)  
# name is a formal parameter during function definition

help(MyFirstFunction)
# Help on function MyFirstFunction in module __main__:
# MyFirstFunction(name)
#    name is a formal parameter during function definition

function parameter

Python functions have very flexible and diverse parameter forms, which can implement simple calls and pass in very complex parameters. The parameters from simple to complex are as follows:

  • positional argument
  • default argument
  • variable argument
  • keyword argument
  • name keyword argument
  • parameter combination

1. Positional parameters

def functionname(arg1):
    "function_docstring"
    function_suite
    return [expression]
  • arg1 - Positional arguments, which are to be fixed in position when calling the function.

2. Default parameters

def functionname(arg1, arg2=v):
    "function_docstring"
    function_suite
    return [expression]
  • arg2 = v - default parameter = default value, when calling the function, if the value of the default parameter is not passed in, it is considered to be the default value.
  • The default parameters must be placed after the positional parameters, otherwise the program will report an error.

[example]

def printinfo(name, age=8):
    print('Name:{0},Age:{1}'.format(name, age))


printinfo('pony')  # Name: pony, Age:8
printinfo('pony', 10)  # Name: pony, Age:10
  • Python allows functions to be called in a different order than they were declared, because the Python interpreter can match parameter values ​​with parameter names.

[example]

def printinfo(name, age):
    print('Name:{0},Age:{1}'.format(name, age))


printinfo(age=8, name='pony')  # Name: pony, Age:8

3. Variable parameters

As the name implies, a variable parameter means that the number of parameters passed in is variable, which can be 0, 1, 2 to any number, and it is a parameter of indefinite length.

def functionname(arg1, arg2=v, *args):
    "function_docstring"
    function_suite
    return [expression]
  • *args - Variable arguments, from zero to any, automatically assembled into tuples.
  • Variable names with an asterisk (*) hold all unnamed variable arguments.

[example]

def printinfo(arg1, *args):
    print(arg1)
    for var in args:
        print(var)


printinfo(10)  # 10
printinfo(70, 60, 50)

# 70
# 60
# 50

4. Keyword Arguments

def functionname(arg1, arg2=v, *args, **kw):
    "function_docstring"
    function_suite
    return [expression]
  • **kw - keyword arguments, can be from zero to any, automatically assembled into a dictionary.

[example]

def printinfo(arg1, *args, **kwargs):
    print(arg1)
    print(args)
    print(kwargs)


printinfo(70, 60, 50)
# 70
# (60, 50)
# {}
printinfo(70, 60, 50, a=1, b=2)
# 70
# (60, 50)
# {'a': 1, 'b': 2}

The similarities and differences between "variadic parameters" and "keyword parameters" are summarized as follows:

  • Variadic parameters allow to pass zero to any number of parameters, which are automatically assembled into a tuple when the function is called.
  • Keyword arguments allow passing zero to any arguments, which are automatically assembled into a dict inside the function.

5. Named keyword arguments

def functionname(arg1, arg2=v, *args, *, nkw, **kw):
    "function_docstring"
    function_suite
    return [expression]
*, nkw - Named keyword parameters, the keyword parameters that the user wants to enter, are defined in nkw prepend a delimiter *. 
  • If you want to restrict the names of keyword arguments, you can use "named keyword arguments"
  • When using named keyword arguments, take special care that the argument name is not missing.

[example]

def printinfo(arg1, *, nkw, **kwargs):
    print(arg1)
    print(nkw)
    print(kwargs)


printinfo(70, nkw=10, a=1, b=2)
# 70
# 10
# {'a': 1, 'b': 2}

printinfo(70, 10, a=1, b=2)
# TypeError: printinfo() takes 1 positional argument but 2 were given
  • The parameter name nwk is not written, so 10 is regarded as a "positional parameter", and the original function has only 1 positional function, and now 2 are called, so the program will report an error.

6. Parameter combination

To define a function in Python, you can use positional parameters, default parameters, variable parameters, named keyword parameters, and keyword parameters. Four of these five parameters can be used together, but note that the order of parameter definitions must be:

  • Positional parameters, default parameters, variadic parameters, and keyword parameters.
  • Positional arguments, default arguments, named keyword arguments, and keyword arguments.

Note the syntax for defining variadic and keyword arguments:

  • *args is a variable parameter, and args receives a tuple
  • **kw is a keyword parameter, and kw receives a dict

Named keyword arguments are intended to limit the parameter names that the caller can pass in, and to provide default values. Don't forget to write the delimiter * when defining named keyword parameters, otherwise it defines positional parameters.

Warning: While up to 5 arguments can be combined, don't use too many at the same time or the function will be hard to understand.

the return value of the function

[example]

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


print(add(1, 2))  # 3
print(add([1, 2, 3], [4, 5, 6]))  # [1, 2, 3, 4, 5, 6]

[example]

def back():
    return [1, 'Pony's program life', 3.14]


print(back())  # [1, 'Pony's Programmed Life', 3.14]

[example]

def back():
    return 1, 'Pony's program life', 3.14


print(back())  # (1, 'Pony's Programmed Life', 3.14)

[example]

def printme(str):
    print(str)

temp = printme('hello') # hello
print(temp) # None
print(type(temp))  # <class 'NoneType'>

variable scope

  • In Python, the variables of the program are not accessible everywhere, and the access rights depend on where the variable is assigned.
  • A variable defined inside a function has a local scope, and the variable is called a local variable.
  • A variable defined outside a function has a global scope and is called a global variable.
  • Local variables can only be accessed within the function in which they are declared, while global variables can be accessed program-wide.

[example]

def discounts(price, rate):
    final_price = price * rate
    return final_price


old_price = float(input('Please enter original price:'))  # 98
rate = float(input('Please enter a discount rate:'))  # 0.9
new_price = discounts(old_price, rate)
print('The price after discount is:%.2f' % new_price)  # 88.20
  • The global and nonlocal keywords are used when the inner scope wants to modify a variable in the outer scope.

[example]

num = 1


def fun1():
    global num  # needs to be declared with the global keyword
    print(num)  # 1
    num = 123
    print(num)  # 123


fun1()
print(num)  # 123

inline function

[example]

def outer():
    print('outer function is called here')

    def inner():
        print('inner function is called here')

    inner()  # This function can only be called inside the outer function


outer()
# The outer function is called here
# The inner function is called here

Closure

  • It is an important grammatical structure of functional programming and a special embedded function.
  • If a reference is made to an outer non-global scope variable within an inner function, the inner function is considered a closure.
  • Through the closure, you can access variables in the outer non-global scope, which is called the closure scope.

[example]

def funX(x):
    def funY(y):
        return x * y

    return funY


i = funX(8)
print(type(i))  # <class 'function'>
print(i(5))  # 40

[Example] The return value of a closure is usually a function.

def make_counter(init):
    counter = [init]

    def inc(): counter[0] += 1

    def dec(): counter[0] -= 1

    def get(): return counter[0]

    def reset(): counter[0] = init

    return inc, dec, get, reset


inc, dec, get, reset = make_counter(0)
inc()
inc()
inc()
print(get())  # 3
dec()
print(get())  # 2
reset()
print(get())  # 0
[Example: If you want to modify the variables in the closure scope, you need to nonlocal keywords
def outer():
    num = 10

    def inner():
        nonlocal num  # nonlocal keyword declaration
        num = 100
        print(num)

    inner()
    print(num)


outer()

# 100
# 100

recursion

  • A function is recursive if it internally calls itself.

[Example] n! = 1 x 2 x 3 x ... x n

# use cycle
n = 5
for k in range(1, 5):
    n = n * k
print(n)  # 120

# use recursion
def factorial(n):
    if n == 1:
        return 1
    return n * factorial(n - 1)


print(factorial(5)) # 120

[Example] Fibonacci sequence f(n)=f(n-1)+f(n-2), f(0)=0 f(1)=1

# use cycle
i = 0
j = 1
lst = list([i, j])
for k in range(2, 11):
    k = i + j
    lst.append(k)
    i = j
    j = k
print(lst)  
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

# use recursion
def recur_fibo(n):
    if n <= 1:
        return n
    return recur_fibo(n - 1) + recur_fibo(n - 2)


lst = list()
for k in range(11):
    lst.append(recur_fibo(k))
print(lst)  
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

[Example] Set the number of recursive layers. The default recursive layer in Python is 100.

import sys

sys.setrecursionlimit(1000)

For a detailed introduction to recursion, see:

2. Lambda expressions

Definition of Anonymous Functions

There are two types of functions in Python:

  • Category 1: Regular functions defined with the def keyword
  • Type 2: Anonymous functions defined with the lambda keyword

python uses the lambda keyword to create anonymous functions instead of the def keyword, it has no function name, and its syntax is as follows:

lambda argument_list: expression
  • lambda - keyword for defining anonymous functions.
  • argument_list - function arguments, which can be positional arguments, default arguments, keyword arguments, and have the same argument types as in regular functions.
  • :- colon, add a colon between function parameters and expressions.
  • expression - is just an expression that takes in function arguments and outputs some value.

Notice:

  • There is no return statement in expression because the lambda doesn't need it to return, the result of the expression itself is the return value.
  • An anonymous function has its own namespace and cannot access parameters outside its parameter list or in the global namespace.

[example]

def sqr(x):
    return x ** 2


print(sqr)
# <function sqr at 0x000000BABD3A4400>

y = [sqr(x) for x in range(10)]
print(y)
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

lbd_sqr = lambda x: x ** 2
print(lbd_sqr)
# <function <lambda> at 0x000000BABB6AC1E0>

y = [lbd_sqr(x) for x in range(10)]
print(y)
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]


sumary = lambda arg1, arg2: arg1 + arg2
print(sumary(10, 20))  # 30

func = lambda *args: sum(args)
print(func(1, 2, 3, 4, 5))  # 15

Use of anonymous functions

Functional programming means that every block of code is immutable and consists of pure functions. The pure function here means that the functions themselves are independent of each other and do not affect each other. For the same input, there will always be the same output without any side effects.

[Example] Non-functional programming

def f(x):
    for i in range(0, len(x)):
        x[i] += 10
    return x


x = [1, 2, 3]
f(x)
print(x)
# [11, 12, 13]

[Example] Functional programming

def f(x):
    y = []
    for item in x:
        y.append(item + 10)
    return y


x = [1, 2, 3]
f(x)
print(x)
# [1, 2, 3]

Anonymous functions are often used in high-order functions in functional programming, and there are two main forms:

  • Arguments are functions (filter, map)
  • The return value is a function (closure)

For example, application in filter and map functions:

  • filter(function, iterable) Filter the sequence, filter out the elements that do not meet the conditions, and return an iterator object. If you want to convert it to a list, you can use list() to convert it.

[example]

odd = lambda x: x % 2 == 1
templist = filter(odd, [1, 2, 3, 4, 5, 6, 7, 8, 9])
print(list(templist))  # [1, 3, 5, 7, 9]
map(function, *iterables) Map the specified sequence according to the provided function.

[example]

m1 = map(lambda x: x ** 2, [1, 2, 3, 4, 5])
print(list(m1))  
# [1, 4, 9, 16, 25]

m2 = map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10])
print(list(m2))  
# [3, 7, 11, 15, 19]

In addition to these built-in functions of Python, we can also define higher-order functions ourselves.

[example]

def apply_to_list(fun, some_list):
    return fun(some_list)

lst = [1, 2, 3, 4, 5]
print(apply_to_list(sum, lst))
# 15

print(apply_to_list(len, lst))
# 5

print(apply_to_list(lambda x: sum(x) / len(x), lst))
# 3.0

 

Posted by amargharat on Wed, 25 May 2022 22:58:27 +0300