Loading

函数与lambda表达式


返回 我的技术栈(Technology Stack)



定义函数

函数名每个单词小写,单词与单词之间使用下划线分隔

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

# 定义一个函数,声明一个形参
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("孙悟空")) # 

return返回的值既可以是有值的变量,也可以是一个表达式

def my_max(x, y) :
	# 返回一个表达式
	return x if x > y else y

为函数提供文档

为函数编写说明文档——只要把一段字符串放在函数声明之后、函数体之前,这段字符串将被作为函数的部分,这个文档就是函数的说明文档

之后即可以通过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("********")
print(my_max.__doc__)


输出:

my_max(x, y)
    获取两个数值之间//////较大数的函数。
    
    my_max(x, y)
        返回x、y两个参数之间较大的那个

********

    获取两个数值之间//////较大数的函数。

    my_max(x, y)
        返回x、y两个参数之间较大的那个

多个返回值

如果程序需要多个返回值,即可以包装成列表返回,也可以直接返回多个值【注意:直接返回多个值,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)  # (76.9, 12.816666666666668)

# 使用序列解包来获取多个返回值
s, avg = sum_and_avg(my_list) #②
print(s)  # 76.9
print(avg)  #  12.816666666666668

函数的参数

形参:定义函数时,小括号中的参数,用来接收参数,在函数内部作为变量使用
实参:调用函数时,小括号中的参数,用来把数据 传递到函数内部

关键字(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))

参数默认值

在某些情况,程序需要在定义函数时为一个或多个形式参数指定默认值——这样在调用函数时,就可以省略为该形参传入参数值,而直接使用该形参的默认值

# 为两个参数指定默认值
def say_hi(name="111", message="欢迎来到疯狂软件"):
    print(name, ", 您好 ,", "消息是:", message)


# 全部使用默认参数
say_hi()  # 111 , 您好 , 消息是: 欢迎来到疯狂软件


# 只有message参数使用默认值
say_hi("222") # 222 , 您好 , 消息是: 欢迎来到疯狂软件


# 两个参数都不使用默认值
say_hi("222", "滚!")  # 222 , 您好 , 消息是: 滚!


# 只有name参数使用默认值
say_hi(message="滚!")  #  111 , 您好 , 消息是: 滚!

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

say_hi("滚!")  # 滚! , 您好 , 消息是: 欢迎来到疯狂软件

say_hi(name="222", "滚!")  【报错!!!因为是 位置参数 必须在 关键字参数 前面】

say_hi("滚!" , name="222")  【报错!!!
即便满足 位置参数 在 关键字参数 前面,
但是由于第一个参数没有指定关键字参数,会传给name,第二个参数使用关键字参数,再次传给name,也就是传了两次name值!
name参数传入了多个参数值,会提示say_hi() got mutiple values for argument 'name'】


下面才是正确的!
       位置参数  关键字参数
say_hi("222", message="滚!") # 222 , 您好 , 消息是: 滚!
say_hi(name="222", message="滚!") # 222 , 您好 , 消息是: 滚!

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讲义")


输出:
( "疯狂iOS讲义", "疯狂Android讲义")
疯狂iOS讲义
疯狂Android讲义
5

Python允许个数可变的形参可以处于形参列表的任意位置,但是要求一个函数最多只能带一个支持“普通”参数收集的形参

如果需要给后面的参数传入参数值,则必须使用关键字参数;否则,程序会把所有传入的多个值都当成是传给books的参数

# 定义了支持参数收集的函数
def test(*books ,num) :
    print(books)
    # books被当成元组处理
    for b in books :
        print(b)
    print(num)
    
    
# 调用test()函数
test("疯狂iOS讲义", "疯狂Android讲义", num = 20)  # 如果需要给后面的参数传入参数值,则必须使用关键字参数


print("*****")
my_list = ["疯狂Swift讲义", "疯狂Python讲义"]
# 将列表的多个元素传给支持参数收集的参数
test(my_list, num = 20)


my_tuple= ("疯狂Swift讲义", "疯狂Python讲义")
# 将元组的多个元素传给支持参数收集的参数
test(*my_tuple, num = 20)

输出:
('疯狂iOS讲义', '疯狂Android讲义')
疯狂iOS讲义
疯狂Android讲义
20
*****
(['疯狂Swift讲义', '疯狂Python讲义'],)
['疯狂Swift讲义', '疯狂Python讲义']
20
('疯狂Swift讲义', '疯狂Python讲义')
疯狂Swift讲义
疯狂Python讲义
20


my_list = ["疯狂Swift讲义", "疯狂Python讲义"]
# 将列表的多个元素传给支持参数收集的参数
test(*my_list, num=20)

输出:
('疯狂Swift讲义', '疯狂Python讲义')
疯狂Swift讲义
疯狂Python讲义
20

一个函数可同时包含一个支持“普通”参数收集的参数,和一个支持关键字参数收集的参数

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


test(1, 2, "疯狂iOS讲义" , "疯狂Android讲义", 语文=89, 数学=94)
输出:
1 2  "疯狂iOS讲义"
("疯狂Android讲义",)
{“语文”:89, “数学”:94}


test(1, 2, 语文=89, 数学=94)
输出:
1 2 3
()
{“语文”:89, “数学”:94}

逆向参数收集

逆向参数收集:是指在程序已有列表、元组、字典等对象的前提下,把他们的元素“拆开”后传给函数的参数
逆向参数收集需要在传入的列表、元组、参数之前添加一个星号,在字典参数之前添加两个星号

def test(name, message):
    print("用户是: ", name)
    print("欢迎消息: ", message)
    
    
my_list = ['孙悟空', '欢迎来疯狂软件']
test(*my_list)
def foo(name, *nums):
    print("name参数: ", name)
    print("nums参数: ", nums)
    
    
my_tuple = (1, 2, 3)
# 使用逆向收集,将my_tuple元组的元素传给nums参数
foo('fkit', *my_tuple)

输出:
name参数:fkit
nums参数:(1,2,3)


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

输出:
name参数:1
nums参数:(2,3)

字典也支持逆向收集

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)

输出:
疯狂Python讲义  这本书的价格是:  89
描述信息 这是一本系统全面的Python学习图书

函数的参数传递机制

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

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)


输出:
swap函数里,a的值是 9 ;b的值是 6
交换结束后,变量a的值是 6 ;变量b的值是 9

变量作用域

局部变量: 在函数中定义的变量,包括参数,都被称为局部变量【函数结束,局部变量失效】
全局变量: 在函数外面、全局范围内定义的变量,被称为全局变量

  • globals():返回 全局范围 内所有变量组成的“变量字典”
    【globals()无论在哪里执行,总是获取全局范围内所有变量组成的“变量字典”】

  • locals():返回 当前局部范围 内所有变量组成的“变量字典”
    【如果在全局范围内(函数之外)调用locals(),同样会获取全局范围内所有变量组成的“变量字典”】

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

注意:
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

局部变量遮蔽(hide)全局变量的情形

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

报错原因:
Python语法规定,在函数内部对不存在的变量赋值时,默认就是重新定义新的局部变量。因为 name = '孙悟空' 相当于重新定义了name局部变量,这样name全局变量name = 'Charlie'就被遮蔽了,所以打印报错!!!

为了解决上述问题,可以使用以下两个方案:

访问被遮蔽的全局变量

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)  # 孙悟空

局部函数

Python支持在函数体内定义函数,这种被放在函数体内定义的函数被称为 局部函数
默认情况下,局部函数对外部是隐藏的,局部函数只能在其封闭(enclosing)函数内有效,其封闭函数也可以返回局部函数,以便于程序在其他作用域中使用局部函数

# 定义函数,该函数会包含局部函数
def 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()
    print(name)


foo()

输出:
Charlie
孙悟空

函数变量

Python的函数也是一种值:
所有的函数都是function对象,这意味着可以把函数本身赋值给变量,就像整数、浮点数、列表、元组等赋值给变量一样。

# 定义一个计算乘方的函数
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

注意:
Python内置的计算乘方的方法:

  1. a**b
  2. pow(a,b)

使用函数作为函数形参

应用背景:有时候需要定义一个函数,该函数的大部分逻辑都能确定,但某些处理逻辑暂时无法确定——这意味着某些程序代码需要动态改变,如果希望调用函数时能够动态传入这些代码,那么就需要在函数中定义函数形参,这样即可以在调用函数时传入不同的函数作为参数,从而动态改变这段代码

# 定义函数类型的形参,其中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))

输出:
原数据:  [3, 4, 9, 5, 8]
计算数组元素的平方
[9, 16, 81, 25, 64]
计算数组元素的立方
[27, 64, 729, 125, 512]
计算数组元素的阶乘
[6, 24, 362880, 120, 40320]
<class 'function'>

使用函数作为返回值

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

局部函数与lambda表达式

回顾局部函数

# 定义函数,该函数会包含局部函数
def 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

由于局部函数的作用域默认停留在其封闭函数之内,因此这三个局部函数的函数名的作用太有限了——仅仅是在程序的if语句中作为返回值使用,一旦离开了get_math_func()函数体,这三个局部函数的函数名就失去了意义。
既然局部函数的函数名没有太大意义,那么就考虑使用lambda表达式来简化局部函数的写法

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




a = lambda x, y: x + y

其函数形式如下:

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

lambda表达式的本质就是匿名的、单行函数体的函数
只能创建简单的函数对象(它只适合函数体为单行的情形)
两个优点:
1、对于单行函数,使用lambda表达式省去了定义函数的过程,让代码更简洁
2、对于不需要多次复用的函数,使用lambda表达式可以在用完之后立即释放,提高了性能

调用Python内置的map()函数
# 传入计算平方的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]

posted @ 2021-05-14 09:31  言非  阅读(85)  评论(0编辑  收藏  举报