Python Programming

Lecture 7 Advanced Functions

7.1 Functions (3)

arbitrary arguments, generator, Lambda function


def make_pizza(toppings):
    print(toppings)

make_pizza('pepperoni')
  • If we want to pass multiple arguments, we may create a list at first.


x=['mushrooms', 'green peppers', 'extra cheese']
def make_pizza(toppings):
    for y in toppings:
        print(y, end=', ')

make_pizza(x)

def make_pizza(topping_1, topping_2, topping_3):
    print(topping_1, topping_2, topping_3)

make_pizza('mushrooms', 'green peppers', 'extra cheese')
  • However, sometimes we are not sure about the exact number of the arguments.

  • We can pass multiple arguments at once in the following way.


def make_pizza(*toppings):
    print(toppings)

make_pizza('pepperoni')
make_pizza('mushrooms', 'green peppers', 'extra cheese')
  • Note that Python packs the arguments into a tuple, even if the function receives only one value.


def make_pizza(*toppings):
    print("\nMaking a pizza with the following toppings:")
    for topping in toppings:
        print("- " + topping)

make_pizza('pepperoni')
make_pizza('mushrooms', 'green peppers', 'extra cheese')

Making a pizza with the following toppings:
- pepperoni
Making a pizza with the following toppings:
- mushrooms
- green peppers
- extra cheese
  • Mixing Positional and Arbitrary Arguments

  • If you want a function to accept several different kinds of arguments, the parameter that accepts an arbitrary number of arguments must be placed last in the function definition.


def make_pizza(size, *toppings):
    print("\nMaking a " + str(size) +
        "-inch pizza with the following toppings:")
    for topping in toppings:
        print("- " + topping)

make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')

Making a 16-inch pizza with the following toppings:
- pepperoni

Making a 12-inch pizza with the following toppings:
- mushrooms
- green peppers
- extra cheese
  • Using Arbitrary Keyword Arguments


def build_profile(**user_info):
    print(user_info)

build_profile(location='Shanghai',field='Management')

{'location': 'Shanghai', 'field': 'Management'}
  • It builds up a dictionary.


def build_profile(**user_info):
    profile={}
    for key, value in user_info.items():
        profile[key] = value
    return profile

user_profile = build_profile(location='Shanghai',
                            field='Management')
print(user_profile)
  • Using Arbitrary Keyword Arguments


def build_profile(first, last, **user_info):
    profile = {}
    profile['first_name'] = first
    profile['last_name'] = last
    for key, value in user_info.items():
        profile[key] = value
    return profile

user_profile = build_profile('albert', 'einstein',
                            location='princeton',
                            field='physics')
print(user_profile)

{'first_name': 'albert', 'last_name': 'einstein',
'location': 'princeton', 'field': 'physics'}

def build_profile(*name, **user_info):
    print(name)
    print(user_info)

build_profile('albert', 'einstein',
               location='princeton',
               field='physics')

('albert', 'einstein')
{'location': 'princeton', 'field': 'physics'}
  • As a tradition, we often use *args and **kw

  • Example


def test(x,y=1,*a,**b):
    print(x,y,a,b)

test(1)
test(1,2)
test(1,2,3)
test(1,2,3,4)
test(x=1,y=2)
test(1,a=2)
test(1,2,3,a=4)
test(1,2,3,y=4)
  • Four ways of creating a list


def test1():
    l = []
    for i in range(100):
        l = l + [i]

def test2():
    l = []
    for i in range(100):
        l.append(i)

def test3():
    l = list(range(100))

def test4():
    l = [i for i in range(100)]

>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]

>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']

>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> [k + '=' + v for k, v in d.items()]
['y=B', 'x=A', 'z=C']

>>> L = ['Hello', 'World', 'IBM', 'Apple']
>>> [s.lower() for s in L]
['hello', 'world', 'ibm', 'apple']
  • Generator


>>> L = [x * x for x in range(4)]
>>> L
[0, 1, 4, 9]
>>> g = (x * x for x in range(4)) #not tuple!
>>> g
 at 0x1022ef630>

>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
Traceback (most recent call last):
  File "", line 1, in 
StopIteration

g = (x * x for x in range(10))
for n in g:
    print(n)

def yrange(n):
    i = 0
    while i < n:
        print(i) 
        i += 1 # i=i+1

yrange(5)

def yrange(n):
    i = 0
    while i < n:
        yield i
        i += 1 

o = yrange(5)

print(next(o))
# output: 0

print(next(o))
# output: 1

print(next(o))
# output: 2

def yrange(n):
    i = 0
    while i < n:
        yield i
        i += 1 

for x in yrange(5):
    print(x)
  • map()


def f(x):
    return x * x

y = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])    
print(type(y))
print(list(y))

map
[1, 4, 9, 16, 25, 36, 49, 64, 81]

print(list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])))
['1', '2', '3', '4', '5', '6', '7', '8', '9']

Anonymous function: lambda


def add( x, y ):
    return x + y
 
lambda x, y: x + y
 
lambda x, y = 2: x + y
lambda *z: z
 
>>> a = lambda x, y: x + y
>>> a( 1, 3 )
4
>>> b = lambda x, y = 2: x + y
>>> b( 1 )
3
>>> b( 1, 3 )
4
>>> c = lambda *z: z
>>> c( 10, 'test')
(10, 'test')
  • Sometimes the anonymous function is convenient.

  • It has only one expression. (You do not have to use return)


sum = lambda arg1, arg2: arg1 + arg2

print ("The total is : ", sum( 10, 20 ))
print ("The total is : ", sum( 20, 20 ))

>>> list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
[1, 4, 9, 16, 25, 36, 49, 64, 81]

def f(x):
    return x * x

7.2 Functions (4)

Return a function, Recursion Intro

  • Return a function

def calc_sum(*args):
    ax = 0
    for n in args:
        ax = ax + n
    return ax

def lazy_sum(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum

f = lazy_sum(1, 3, 5, 7, 9)
print(f()) # print(f) causes error
print(type(f)) # delete lazy_sum, f still exists.

25
function

f1 = lazy_sum(1, 3, 5, 7, 9)
f2 = lazy_sum(1, 3, 5, 7, 9)
print(f1==f2)

#output
False
  • Defining a closure function

  • When do we have a closure?

    • We must have a nested function (function inside a function).

    • The nested function must refer to a value defined in the enclosing function.

    • The enclosing function must return the nested function.

  • When to use closures?

    • Closures can avoid the use of global values and provides some form of data hiding. It can also provide an object oriented solution to the problem.

Example


def print_msg():
    msg = "zen of python"
    def printer():
        return msg
    return printer

another = print_msg()
print(another())

def make_multiplier_of(n):
    def multiplier(x):
        return x * n
    return multiplier

# Multiplier of 3
times3 = make_multiplier_of(3)

# Multiplier of 5
times5 = make_multiplier_of(5)

# Output: 27
print(times3(9))

# Output: 15
print(times5(3))

# Output: 30
print(times5(times3(2)))

Recursion Brief Intro

  • Calculate: Factorial $n!$


def fact(n):
    if n==1:
        return 1
    else:
        return n * fact(n - 1)

===> fact(5)
===> 5 * fact(4)
===> 5 * (4 * fact(3))
===> 5 * (4 * (3 * fact(2)))
===> 5 * (4 * (3 * (2 * fact(1))))
===> 5 * (4 * (3 * (2 * 1)))
===> 5 * (4 * (3 * 2))
===> 5 * (4 * 6)
===> 5 * 24
===> 120

Summary

  • Functions
    • Reading: Python for Everybody, Chapter 10.1-10.5, 10.7-10.8
    • Reading: Python Crash Course, Chapter 8