在之前的课程中,我们讲到过关于函数的知识,我们还讲到过Python中常用的数据类型,这些类型的变量都可以作为函数的参数或返回值;通过前几节课的学习,我们又知道了写在类中的函数通常称之为方法,它代表了类或者对象可以接收的消息。如果我们把这些知识汇总一下,我们的函数就可以做更多的事情。
创新互联建站专注于横峰企业网站建设,响应式网站建设,商城网站定制开发。横峰网站建设公司,为横峰等地区提供建站服务。全流程按需搭建网站,专业设计,全程项目跟踪,创新互联建站专业和态度为您提供的服务
关键字参数
下面是一个判断传入的三条边长能否构成三角形的函数,在调用函数传入参数时,我们可以指定参数名,也可以不指定参数名,代码如下所示。
def can_form_triangle(a, b, 
c):
   
print(f'a = {a}, b = {b}, 
c = {
c}')
   
return a + b > 
c and b + 
c > a and a + 
c > b
# 调用函数传入参数不指定参数名按位置对号入座
print(can_form_triangle(
1, 
2, 
3))
# 调用函数通过“参数名=参数值”的形式按顺序传入参数
print(can_form_triangle(a=
1, b=
2, 
c=
3))
# 调用函数通过“参数名=参数值”的形式不按顺序传入参数
print(can_form_triangle(
c=
3, a=
1, b=
2))在没有特殊处理的情况下,函数的参数都是 位置参数,也就意味着传入参数的时候对号入座即可,如上面代码的第7行所示,传入的参数值1、2、3会依次赋值给参数a、b、c。当然,也可以通过参数名=参数值的方式传入函数所需的参数,因为指定了参数名,传入参数的顺序可以进行调整,如上面代码的第9行和第11行所示。
调用函数时,如果希望函数的调用者必须以参数名=参数值的方式传参,可以用 命名关键字参数取代位置参数。所谓命名关键字参数,是在函数的参数列表中,写在*之后的参数,代码如下所示。
def 
can_form_triangle
(*, a, b, c):
   print(
f'a = 
{a}, b = 
{b}, c = 
{c}')
   
return a + b > c 
and b + c > a 
and a + c > b
# TypeError: can_form_triangle() takes 0 positional arguments but 3 were given
# print(is_valid_for_triangle(3, 4, 5))
# 传参时必须使用“参数名=参数值”的方式,位置不重要
print(can_form_triangle(a=3, b=4, c=5))
print(can_form_triangle(c=5, b=4, a=3))
注意:上面的can_form_triangle函数,参数列表中的*是一个分隔符,*前面的参数都是位置参数,而*后面的参数就是命名关键字参数。
我们之前讲过在函数的参数列表中可以使用 可变参数*args来接收任意数量的参数,但是我们需要看看,*args是否能够接收带参数名的参数。
def 
calc
(*args):
   result = 
0
   
for arg 
in args:
       result += arg
   
return result
print(calc(a=
1, b=
2, c=
3))执行上面的代码会引发TypeError错误,错误消息为calc() got an unexpected keyword argument 'a',由此可见,*args并不能处理带参数名的参数。我们在设计函数时,如果既不知道调用者会传入的参数个数,也不知道调用者会不会指定参数名,那么同时使用可变参数和 关键字参数。关键字参数会将传入的带参数名的参数组装成一个字典,参数名就是字典中键值对的键,而参数值就是字典中键值对的值,代码如下所示。
def calc(*args, **kwargs):
   result = 
0
   
for 
arg 
in args:
       result += 
arg
   
for value 
in kwargs.values():
       result += value
   
return total
print(calc())                  # 
0
print(calc(
1, 
2, 
3))           # 
6
print(calc(a=
1, b=
2, c=
3))     # 
6
print(calc(
1, 
2, c=
3, d=
4))    # 
10
提示: 不带参数名的参数(位置参数)必须出现在带参数名的参数(关键字参数)之前,否则将会引发异常。例如,执行calc(1, 2, c=3, d=4, 5)将会引发SyntaxError错误,错误消息为positional argument follows keyword argument,翻译成中文意思是“位置参数出现在关键字参数之后”。

高阶函数的用法
在前面几节课中,我们讲到了面向对象程序设计,在面向对象的世界中,一切皆为对象,所以类和函数也是对象。函数的参数和返回值可以是任意类型的对象,这就意味着 函数本身也可以作为函数的参数或返回值,这就是所谓的 高阶函数。
如果我们希望上面的calc函数不仅仅可以做多个参数求和,还可以做多个参数求乘积甚至更多的二元运算,我们就可以使用高阶函数的方式来改写上面的代码,将加法运算从函数中移除掉,具体的做法如下所示。
def 
calc(
*args, init_value, op, **kwargs):
    result = init_value
   
for arg 
in args:
       result = op(result, arg)
   
for 
value 
in kwargs.values():
       result = op(result, 
value)
   
return result注意,上面的函数增加了两个参数,其中init_value代表运算的初始值,op代表二元运算函数。经过改造的calc函数不仅仅可以实现多个参数的累加求和,也可以实现多个参数的累乘运算,代码如下所示。
def 
add
(x, y):
   
return x + y
def mul(x, y):
   
return x * y
print(calc(
1, 
2, 
3, x=
4, y=
5, init_value=
0, op=add))      
# 15
print(calc(1, 2, init_value=1, op=mul, x=3, y=4, z=5))    # 120通过对高阶函数的运用,calc函数不再和加法运算耦合,所以灵活性和通用性会变强,这是编程中一种常用的技巧,但是最初学者来说可能会稍微有点难以理解。需要注意的是,将函数作为参数和调用函数是有显著的区别的, 调用函数需要在函数名后面跟上圆括号,而把函数作为参数时只需要函数名即可。上面的代码也可以不用定义add和mul函数,因为Python标准库中的operator模块提供了代表加法运算的add和代表乘法运算的mul函数,我们直接使用即可,代码如下所示。
import 
operator
print(calc(init_value=0, 
op=operator.add, 
1
, 
2
, 
3
, 
x=4, 
y=5))      
# 15
print(calc(init_value=1, op=operator.mul, 1, 2, x=3, y=4, z=5))    # 120Python内置函数中有不少高阶函数,我们前面提到过的filter和map函数就是高阶函数,前者可以实现对序列中元素的过滤,后者可以实现对序列中元素的映射,例如我们要去掉一个整数列表中的奇数,并对所有的偶数求平方得到一个新的列表,就可以直接使用这两个函数来做到,具体的做法是如下所示。
def 
is_even
(num):
   
return num % 
2 == 
0
def square(num):
   
return num ** 
2
numbers1 = [
35, 
12, 
8, 
99, 
60, 
52]
numbers2 = list(map(square, filter(is_even, numbers1)))
print(numbers2)    
# [144, 64, 3600, 2704]当然,要完成上面代码的功能,也可以使用列表生成式,列表生成式的做法更为简单优雅。
numbers1 
= 
[35, 
12
, 
8
, 
99
, 
60
, 
52
]
numbers2 
= 
[num 
** 
2 
for 
num 
in 
numbers1 
if 
num 
% 
2 
== 
0
]
print(numbers2)    
# [144, 64, 3600, 2704]Lambda函数
在使用高阶函数的时候,如果作为参数或者返回值的函数本身非常简单,一行代码就能够完成,那么我们可以使用 Lambda函数来表示。Python中的Lambda函数是没有的名字函数,所以很多人也把它叫做 匿名函数,匿名函数只能有一行代码,代码中的表达式产生的运算结果就是这个匿名函数的返回值。上面代码中的is_even和square函数都只有一行代码,我们可以用Lambda函数来替换掉它们,代码如下所示。
numbers1 
= 
[35, 
12
, 
8
, 
99
, 
60
, 
52
]
numbers2 
= 
list(map(lambda 
x: 
x 
** 
2
, 
filter(lambda 
x: 
x 
% 
2 
== 
0
, 
numbers1)))
print(numbers2)    
# [144, 64, 3600, 2704]通过上面的代码可以看出,定义Lambda函数的关键字是lambda,后面跟函数的参数,如果有多个参数用逗号进行分隔;冒号后面的部分就是函数的执行体,通常是一个表达式,表达式的运算结果就是Lambda函数的返回值,不需要写return 关键字。
如果需要使用加减乘除这种简单的二元函数,也可以用Lambda函数来书写,例如调用上面的calc函数时,可以通过传入Lambda函数来作为op参数的参数值。当然,op参数也可以有默认值,例如我们可以用一个代表加法运算的Lambda函数来作为op参数的默认值。
def 
calc(
*args, init_value=
0, op=lambda x, y: x + y, **kwargs):
    result = init_value
   
for arg 
in args:
       result = op(result, arg)
   
for 
value 
in kwargs.values():
       result = op(result, 
value)
   
return result
# 调用calc函数,使用init_value和op的默认值
print(calc(1, 2, 3, x=4, y=5))    # 15
# 调用calc函数,通过lambda函数给op参数赋值
print(calc(1, 2, 3, x=4, y=5, init_value=1, op=lambda x, y: x * y))    # 120
提示:注意上面的代码中的calc函数,它同时使用了可变参数、关键字参数、命名关键字参数,其中命名关键字参数要放在可变参数和关键字参数之间,传参时先传入可变参数,关键字参数和命名关键字参数的先后顺序并不重要。
有很多函数在Python中用一行代码就能实现,我们可以用Lambda函数来定义这些函数,调用Lambda函数就跟调用普通函数一样,代码如下所示。
import 
operator, 
functools
# 
一行代码定义求阶乘的函数
fac 
= 
lambda num: 
functools.reduce(operator.mul, 
range(1, 
num 
+ 
1
), 
1
)
# 
一行代码定义判断素数的函数
is_prime 
= 
lambda x: 
x 
> 
1 
and 
all(map(lambda 
f: 
x 
% 
f, 
range(2, 
int(x 
** 
0.5
) 
+ 
1
)))
# 
调用Lambda函数
print(fac(10))        
# 3628800
print(is_prime(9))    # False
提示1:上面使用的reduce函数是Python标准库functools模块中的函数,它可以实现对数据的归约操作,通常情况下, 过滤(filter)、 映射(map)和 归约(reduce)是处理数据中非常关键的三个步骤,而Python的标准库也提供了对这三个操作的支持。
提示2:上面使用的all函数是Python内置函数,如果传入的序列中所有布尔值都是True,all函数就返回True,否则all函数就返回False。
简单的总结
Python中的函数可以使用可变参数*args和关键字参数**kwargs来接收任意数量的参数,而且传入参数时可以带上参数名也可以没有参数名,可变参数会被处理成一个元组,而关键字参数会被处理成一个字典。Python中的函数也是对象,所以函数可以作为函数的参数和返回值,也就是说,在Python中我们可以使用高阶函数。如果我们要定义的函数非常简单,只有一行代码且不需要名字,可以将函数写成Lambda函数(匿名函数)的形式。
网站标题:从零开始学Python:20课-函数使用进阶
本文来源:http://www.scyingshan.cn/article/jgcgee.html

 建站
建站
 咨询
咨询 售后
售后
 建站咨询
建站咨询 
 