Python3.*(第二十课——函数和Lambda表达式)

函数就是python程序的重要组成单位,一个python程序可以由多个函数组成。

通俗来讲所谓的函数,就是指为一段实现特定功能的代码“取”一个名字,以后即可通过该名字来执行(调用)该函数。

从函数定义者的角度来看,其至少需要想清楚以下3点:

           函数需要几个关键的需要动态变化的数据,这些数据应该被定义成函数的参数。

           函数需要传出几个重要的数据(就是调用该函数的人希望得到的数据),这些数据应该被 定义成返回值。

           函数的内部实现过程。

定义函数和调用函数

       语法格式:

              def  函数名    (形参列表)

                     //零条到多条可以执行的语句

                     [return[返回值]]

       函数名:合法标识符。

       形参列表:用于定义该函数可以接收的参数,多个形参名之间以英文逗号(,)隔开。谁调用函数,谁负责为形参赋值

## 定义一个函数,声明2个形参
#def my_max(x, y) : 
#	# 定义一个变量z,该变量等于x、y中较大的值
#	z = x if x > y else y
#	# 返回变量z的值
#	return z
def my_max(x, y) : 
	# 返回一个表达式
	return x if x > y else y

# 定义一个函数,声明一个形参
def say_hi(name) :
	print("===正在执行say_hi()函数===")
	return name + ",您好!"
a = 6
b = 9
# 调用my_max()函数,将函数返回值赋值给result变量
result = my_max(a , b) # ①
print("result:", result)
# 调用say_hi()函数,直接输出函数的返回值
print(say_hi("孙悟空")) # ②

当程序调用一个函数时,既可以把调用函数的返回值赋值给指定变量,也可以将函数的返回值传给另一个函数,作为另一个函数的参数。

运行结果:

result:9
===正在执行say_hi()函数===
孙悟空,您好!

return 语句可以显式地返回一个值,

return 语句返回的值既可是有值的变量, 也可是一个表达式。如下:

def my_max(x,y):

return x if x > y else y

为函数提供文档

       python程序可以使用内置的help()函数查看其他函数的帮助文档,也可以通过函数的__doc__属性访问函数的说明文档。

怎么来编写说明文档?

def my_max(x, y) : 
    '''
    获取两个数值之间较大数的函数。

    my_max(x, y)
        返回x、y两个参数之间较大的那个
    '''
    # 定义一个变量z,该变量等于x、y中较大的值
    z = x if x > y else y
    # 返回变量z的值
    return z
# 使用help()函数查看my_max的帮助文档
help(my_max)
print(my_max.__doc__)

多个返回值

       如果程序需要有多个返回值,则既可将多个值包装成列表之后返回,也可直接返回多个值。如果 Python 函数直接返回多个值, Python 会自动将多个返回值封装成元组。

def sum_and_avg(list):
    sum = 0
    count = 0
    for e in list:
        # 如果元素e是数值
        if isinstance(e, int) or isinstance(e, float):
            count += 1
            sum += e
    return sum, sum / count
my_list = [20, 15, 2.8, 'a', 35, 5.9, -1.8]
# 获取sum_and_avg函数返回的多个值,多个返回值被封装成元组
tp = sum_and_avg(my_list) #①
print(tp)
# 使用序列解包来获取多个返回值
s, avg = sum_and_avg(my_list) #②
print(s)
print(avg)

       利用解包功能:使用多个变量接收函数返回的多个值。

              s,avg = sum_and_avg(my_list)

              print(s)

              print(avg)

递归函数

       在一个函数体内调用它自身,被称为函数递归。递归一定要向己知的方向进行。否则,变成了无穷递归,类似于死循环。

def fn(n) :
	if n == 0 :
		return 1
	elif n == 1 :
		return 4
	else :
		# 函数中调用它自身,就是函数递归
		return 2 * fn(n - 1) + fn(n - 2)
# 输出fn(10)的结果
print("fn(10)的结果是:", fn(10))

5.2函数的参数

关键字(keyword)参数

       按照形参位置传入的参数被称为位置参数,则必须严格按照定义函数时指定的顺序来传入参数值。

       根据参数名来传入参数值,则无须遵守定义形参的顺序,这种方式被称为关键字(keyword)参数

# 定义一个函数
def girth(width , height): 
	print("width: ", width)
	print("height: ", height)
	return 2 * (width + height)
# 传统调用函数的方式,根据位置传入参数
print(girth(3.5, 4.8))
# 根据关键字参数来传入参数
print(girth(width = 3.5, height = 4.8))
# 使用关键字参数时可交换位置
print(girth(height = 4.8, width = 3.5))
# 部分使用关键字参数,部分使用位置参数
print(girth(3.5, height = 4.8))

# 位置参数必须放在关键字参数之前,下面代码错误
print(girth(width = 3.5, 4.8))

如果希望在调用函数时混合使用关键字参数和位置参数,则关键字参数必须位于位置参数之后。换句话说,在关键字参数之后的只能是关键字参数。

如:print(girth(width = 3.5,4.8))

参数默认值

       语法格式如下:

              形参名=默认值

# 为两个参数指定默认值
def say_hi(name = "孙悟空", message = "欢迎来到疯狂软件"):
	print(name, ", 您好")
	print("消息是:", message)
# 全部使用默认参数
say_hi()
# 只有message参数使用默认值
say_hi("白骨精")
# 两个参数都不使用默认值
say_hi("白骨精", "欢迎学习Python")
# 只有name参数使用默认值
say_hi(message = "欢迎学习Python")

say_hi("欢迎学习Python")

#say_hi(name="白骨精", "欢迎学习Python")

#say_hi("欢迎学习Python" , name="白骨精")

say_hi("白骨精", message="欢迎学习Python")
say_hi(name="白骨精", message="欢迎学习Python")

如果只传入一个位置参数,由于该参数位于第一位,系统会将该参数值传给name参数。

       say_hi(name="白骨精", "欢迎学习Python")——错误

       python规定:关键字参数必须位于位置参数的后面。

       say_hi("欢迎学习Python"n,ame="白骨精")——错误

       改为:

       say_hi("白骨精", message="欢迎学习Python")——正确

       say_hi(name="白骨精", message="欢迎学习Python")——正确

定义函数时指定了默认值的参数(关键字参数)必须在没有默认值的参数之后。

# 定义一个打印三角形的函数,有默认值的参数必须放在后面
def printTriangle(char, height = 5) :
	for i in range(1, height + 1) :
	    # 先打印一排空格
	    for j in range(height - i) :
		print(' ', end = '')
	    # 再打印一排特殊字符
	    for j in range(2 * i - 1) :
		print(char, end = '')
	    print()
printTriangle('@', 6)
printTriangle('#', height=7)
printTriangle(char = '*')

Python 要求将带默认值的参数定义在形参列表的最后。

参数收集(个数可变的参数)

在形参前面添加一个星号(*),多个参数值被当成元组传入。允许个数可变的形参可以处于形参列表的任意位置。(不要求是形参列表的最后一个参数)

一个函数最多只能带一个支持“普通”参数收集的形参。

# 定义了支持参数收集的函数
def test(a, *books) :
    print(books)
    # books被当成元组处理
    for b in books :
        print(b)
    # 输出整数变量a的值
    print(a)
# 调用test()函数
test(5 , "疯狂iOS讲义" , "疯狂Android讲义")

如果需要给后面的参数传入参数值,则必须使用关键字参数;

# 定义了支持参数收集的函数
def test(*books ,num) :
    print(books)
    # books被当成元组处理
    for b in books :
        print(b)
    print(num)
# 调用test()函数
test("疯狂iOS讲义", "疯狂Android讲义", num = 20)

num这里必须用关键字参数;

收集关键字参数,需要在参数前面添加两个星号,会将这种关键字参数收集成字典。可同时包含一个支持“普通”参数收集的参数和一个支持关键字参数收集的参数。

# 定义了支持参数收集的函数
def test(x, y, z=3, *books, **scores) :
    print(x, y, z)
    print(books)
    print(scores)
test(1, 2, 3, "疯狂iOS讲义" , "疯狂Android讲义", 语文=89, 数学=94)
test(1, 2, "疯狂iOS讲义" , "疯狂Android讲义", 语文=89, 数学=94)
test(1, 2, 语文=89, 数学=94)

让默认z参数发挥作用的调用方式:test(1, 2, 语文=89, 数学=94)

逆向参数收集

所谓逆向收集:指在程序已有列表的、元组、字典等对象的前提下,把它们的元素“拆开”后传给函数的参数。

逆向收集参数需要在列表、元组参数之前添加一个星号,字典参数之前添加两个星号。

def test(name, message):
    print("用户是: ", name)
    print("欢迎消息: ", message)
my_list = ['孙悟空', '欢迎来疯狂软件']
test(*my_list)

程序需要将一个元组传给该参数,那么同样需要使用逆向收集:

# my_tuple是元组
def foo(name, *nums):
    print("name参数: ", name)
    print("nums参数: ", nums)
my_tuple = (1, 2, 3)
# 使用逆向收集,将my_tuple元组的元素传给nums参数
foo('fkit', *my_tuple)

       另外一种调用方式

                     # 使用逆向收集,将my_tuple元组的第一个元素传给name参数,剩下参数传给nums参数

                     foo(*my_tuple)

不使用逆向收集,my_tuple元组整体传给name参数,(不在元组参数之前添加星号)

                     foo(my_tuple)

字典的逆向收集,字典将会以关键字参数的形式传入。

def bar(book, price, desc):
    print(book, " 这本书的价格是: ", price)
    print('描述信息', desc)
my_dict = {'price': 89, 'book': '疯狂Python讲义', 'desc': '这是一本系统全面的Python学习图书'}
# 按逆向收集的方式将my_dict的多个key-value传给bar()函数
bar(**my_dict)

函数的参数传递机制

def swap(a , b) :
    # 下面代码实现a、b变量的值交换
    a, b = b, a
    print("swap函数里,a的值是", \
        a, ";b的值是", b)
a = 6
b = 9
swap(a , b)
print("交换结束后,变量a的值是", \
    a , ";变量b的值是", b)

解释上面:Python中函数的参数传递机制都是“值传递”。所谓值传递,就是将实际参数值的副本    (复制品)传入函数,而参数本身不会受到任何影响。

def swap(dw):
    # 下面代码实现dw的a、b两个元素的值交换
    dw['a'], dw['b'] = dw['b'], dw['a']
    print("swap函数里,a元素的值是",\
        dw['a'], ";b元素的值是", dw['b'])
    # 把dw直接赋值为None,让它不再指向任何对象
    dw = None
dw = {'a': 6, 'b': 9}
swap(dw)
print("交换结束后,a元素的值是",\
    dw['a'], ";b元素的值是", dw['b'])

主程序中的dw是一个引用变量(也就是一个指针),它保存了字典对象的地址值,当把dw的值赋给swap()函数的dw参数后,就是让swap()函数的dw参数也保存这个地址值,即也会引用到同一个字典对象。

变量的作用域

       局部变量:在函数中定义的变量,包括参数

       全局变量:在函数外面,全局范围内定义的变量

每个函数在执行时,系统都会为该函数分配一块“临时内存空间”,所有的局部变量都被保存在这块临时内存空间内。当函数执行完成后,这块内存空间就被释放了,这些局部变量也就是失效了。因此离开函数之后就不能在访问局部变量。

其实变量和它们的值就像一个“看不见”的字典,其中变量名就是字典的key,变量值就是字典的value。

       Python提供了如下三个工具函数来获取指定范围内的“变量字典”:

              globals():该函数返回全局范围内所有变量组成的“变量字典”。

              locals():该函数返回当前局部范围内所有变量组成的“变量字典”。

              vars(object):获取在指定对象范围内所有变量组成的“变量字典”。如果不传入object参数,vars()和locals()的作用完全相同。

       globals()和locals()区别和联系:
              ①如果在全局范围内(在函数外)调用locals()函数,同样会获取全局范围内所有变

              量组成的“变量字典”。

              ② globals()无论在哪里执行,总是获取全局范围内所有变量组成的“变量字典”。

              ③ globals()和locals()获取“变量字典”都可以被修改,而这种修改会真正改变全局变

              量本身。但通过locals()获取的局部范围内的“变量字典”,即使对它修改也不会影响

              局部变量本身。

def test():
    age = 20
    # 直接访问age局部变量
    print(age) # 输出20
    # 访问函数局部范围的“变量数组”
    print(locals()) # {'age': 20}
    # 通过函数局部范围的“变量数组”访问age变量
    print(locals()['age']) # 20
    # 通过locals函数局部范围的“变量数组”改变age变量的值
    locals()['age'] = 12
    # 再次访问age变量的值
    print('xxx', age) # 依然输出20
    # 通过globals函数修改x全局变量
    globals()['x'] = 19
test();
x = 5
y = 20
print(globals()) # {..., 'x': 5, 'y': 20}
# 在全局访问内使用locals函数,访问的是全局变量的“变量数组”
print(locals()) # {..., 'x': 5, 'y': 20}
# 直接访问x全局变量
print(x) # 5
# 通过全局变量的“变量数组”访问x全局变量
print(globals()['x']) # 5
# 通过全局变量的“变量数组”对x全局变量赋值
globals()['x'] = 39
print(x) # 输出39
# 在全局范围内使用locals函数对x全局变量赋值
locals()['x'] = 99
print(x) # 输出99

④系统会输出默认变量不用管。

全局变量默认可以在所有函数内被访问,但如果在函数中定义了与全局变量同名的变量,此时就发生局部变量遮蔽全局变量的情形。

name = 'Charlie'
def test ():
    # 直接访问name全局变量
    print(name) # Charlie
    name = '孙悟空'
test()
print(name)

报错:UnboundLocalError: local variable 'name' referenced before assignmen

在函数内部对不存在的变量赋值时,默认就是重新定义新的局部变量。这样name全局变量就被遮蔽了,所以上面程序中粗体字代码会报错。

       解决上面的错误:
                     ①访问被遮蔽的全局变量

name = 'Charlie'
def test ():
    # 直接访问name全局变量
    print(globals()['name'])  # Charlie
    name = '孙悟空'
test()
print(name)  # Charlie

            ②在函数中声明全局变量

name = 'Charlie'
def test ():
    # 声明name是全局变量,后面的赋值语句不会重新定义局部变量
    global name
    # 直接访问name全局变量
    print(name)  # Charlie
    name = '孙悟空'
test()
print(name)  # 孙悟空

增加了global name声明之后,程序会把name变量当成全局变量。

5.3局部函数

在函数体内定义函数,这种被放在函数体内定义的函数称为局部函数。

       ①局部函数对外部是隐藏的

       ②只能在其封闭函数内有效

       ③封闭函数也可以返回局部函数

# 定义函数,该函数会包含局部函数
vdef get_math_func(type, nn) :
    # 定义一个计算平方的局部函数
    def square(n) :  # ①
        return n * n
    # 定义一个计算立方的局部函数
    def cube(n) :  # ②
        return n * n * n
    # 定义一个计算阶乘的局部函数
    def factorial(n) :   # ③
        result = 1
        for index in range(2, n + 1) :
            result *= index
        return result
    # 调用局部函数
    if type == "square" :
        return square(nn)
    elif type == "cube":
        return cube(nn)
    else:
        return factorial(nn)
print(get_math_func("square", 3)) # 输出9
print(get_math_func("cube", 3)) # 输出27
print(get_math_func("", 3)) # 输出6

①如果封闭函数没有返回局部函数,那么局部函数只能在封闭函数内部调用。如果封闭函数将局部函数返回,且程序使用变量保存了封闭函数的返回值,那么这些局部函数的作用域就会被扩大。

②局部函数内的变量也会遮蔽它所在函数内的局部变量。

def foo ():
    # 局部变量name
    name = 'Charlie'
    def bar ():
        # 访问bar函数所在的foo函数的name局部变量
        print(name) # Charlie
        name = '孙悟空'
    bar()
foo()

通过nonlocal语句即可声明访问赋值语句只是访问该函数所在函数内的局部变量。改为如下:

def foo ():
    # 局部变量name
    name = 'Charlie'
    def bar ():
        nonlocal name
        # 访问bar函数所在的foo函数的name局部变量
        print(name) # Charlie
        name = '孙悟空'
    bar()
foo()

④ nonlocal关键字用于声明访问当前函数所在函数内部的局部变量。global用于声明访问全局变量。

5.4函数的高级内容

       ①可以用于赋值

       ②用于其他函数的参数

       ③作为其他函数的返回值

使用函数变量

       把函数本身赋值给变量,就像给变量赋值一样。就可以通过该变量来调用函数。对变量赋值不同的函数,让变量在不同的时间指向不通过的函数。

# 定义一个计算乘方的函数
def pow(base, exponent) :
	result = 1
	for i in range(1, exponent + 1) :
		result *= base
	return result
# 将pow函数赋值给my_fun,则my_fun可当成pow使用
my_fun = pow
print(my_fun(3 , 4)) # 输出81
# 定义一个计算面积的函数
def area(width, height) :
	return width * height
# 将area函数赋值给my_fun,则my_fun可当成area使用
my_fun = area
print(my_fun(3, 4)) # 输出12

使用函数作为函数形参

# 定义函数类型的形参,其中fn是一个函数
def map(data, fn) :    
    result = []
    # 遍历data列表中每个元素,并用fn函数对每个元素进行计算
    # 然后将计算结果作为新数组的元素
    for e in data :
        result.append(fn(e))
    return result
# 定义一个计算平方的函数
def square(n) :
    return n * n
# 定义一个计算立方的函数
def cube(n) :
    return n * n * n
# 定义一个计算阶乘的函数
def factorial(n) :
    result = 1
    for index in range(2, n + 1) :
        result *= index
    return result
data = [3 , 4 , 9 , 5, 8]
print("原数据: ", data)
# 下面程序代码3次调用map()函数,每次调用时传入不同的函数
print("计算数组元素的平方")
print(map(data , square))
print("计算数组元素的立方")
print(map(data , cube))
print("计算数组元素的阶乘")
print(map(data , factorial))
# 获取map的类型
print(type(map))

使用函数作为返回值

def get_math_func(type) :
    # 定义一个计算平方的局部函数
    def square(n) :  # ①
        return n * n
    # 定义一个计算立方的局部函数
    def cube(n) :  # ②
        return n * n * n
    # 定义一个计算阶乘的局部函数
    def factorial(n) :   # ③
        result = 1
        for index in range(2 , n + 1): 
            result *= index
        return result
    # 返回局部函数
    if type == "square" :
        return square
    if type == "cube" :
        return cube
    else:
        return factorial
# 调用get_math_func(),程序返回一个嵌套函数
math_func = get_math_func("cube") # 得到cube函数
print(math_func(5)) # 输出125
math_func = get_math_func("square") # 得到square函数
print(math_func(5)) # 输出25
math_func = get_math_func("other") # 得到factorial函数
print(math_func(5)) # 输出120

5.5局部函数与lambda表达式

使用lambda表达式代替局部函数

       ①lambda表达式只能是单行表达式

       ②必须使用lambda关键字定义

       ③lambda关键字之后,冒号左边的是参数列表,可以没有,也可以有多个参数。多个参数,用逗号隔开,冒号右边是该lambda表达式的返回值。

              例如:
                     lambda x,y:x+y

                     改为:

                     def add(x,y) : return x+y

将上一节讲的改为如下:

def get_math_func(type) :
    result=1
    # 该函数返回的是Lambda表达式
    if type == 'square':
        return lambda n: n * n  # ①
    elif type == 'cube':
        return lambda n: n * n * n  # ②
    else:
        return lambda n: (1 + n) * n / 2 # ③
# 调用get_math_func(),程序返回一个嵌套函数
math_func = get_math_func("cube")
print(math_func(5)) # 输出125
math_func = get_math_func("square")
print(math_func(5)) # 输出25
math_func = get_math_func("other")
print(math_func(5)) # 输出15.0

       lambda表达式的两个用途
              省去定义函数的过程,让代码更加简洁

              对于不需要多次复用的函数,使用lambda表达式可以在用完之后立即释放,提高了性能

# 传入计算平方的lambda表达式作为参数
x = map(lambda x: x*x , range(8))
print([e for e in x]) # [0, 1, 4, 9, 16, 25, 36, 49]
# 传入计算平方的lambda表达式作为参数
y = map(lambda x: x*x if x % 2 == 0 else 0, range(8))
print([e for e in y]) # [0, 0, 4, 0, 16, 0, 36, 0]

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值