12.1 定义函数
内置函数(Built-in Functions,BIF):Python官方提供的,用到一些函数,len()、min()和max()。
Python作为解释性语言,其函数必定义后调用。
自定义函数语法结构:
def 函数名(参数列表):
函数体
return 返回值
Python中定义函数时,关键字是def,多个参数列表之间可以用逗号","分隔,当然函数也可以没有参数。如果函数有返回数据,那就需要在函数体最后使用return语句将数据返回;如果没有数据返回,则函数体中可以使用return None或省略return语句。
def rectangle_area(width, height):
area = width * height
return area
r_area = rectangle_area(320.0, 480.0)
print('320x480的长方形的面积:{0:.2f}'.format(r_area))
定义计算长方形面积的函数rectangle_area,它有两个参数,分别是长方形的宽和高,width和height是参数名。
代码通过return返回函数计算结果。
12.2 函数参数
12.2.1 使用关键字参数调用函数
def print_area(width, height):
area = width * height
print('{0} x {1} 长方形的面积:{2}'.format(width, height, area))
print_area(320.0, 480.0) # 没有采用关键字参数函数调用
print_area(width=320.0, height=480.0) # 采用关键字参数函数调用
print_area(320.0, height=480.0) # 采用关键字参数函数调用
# print_area(width=320.0, height) # 发生错误
print_area(height=480.0, width=320.0) # 采用关键字参数函数调用
采用关键字参数函数调用时,参数顺序可以与函数定义时的参数顺序不同。
12.2.2 参数默认值
在定义参数时可以为参数设置一个默认值,调用函数时可以忽略该参数。
def make_coffee(name="卡布奇诺"):
return "制作一杯{0}咖啡。".format(name)
coffee1 = make_coffee("拿铁")
coffee2 = make_coffee()
print(coffee1) # 制作一杯拿铁咖啡
print(coffee2) # 制作一杯卡布奇诺咖啡
上述代码定义了make_coffee函数,其中把卡布奇诺设置为默认值。在参数列表中,默认值可以跟在参数类型后面,通过等号提供给参数。在调试时,如果没有调用者没有传递函数,则使用默认值。
提示:在Java语言中make_coffee函数可以采用重载实现多个版本。Python不支持函数重载,而是使用参数默认值的方式提供函数重载的功能。因为参数默认值只需要定义一个函数就可以了,而重载需要定义多个函数,这会增加代码量。
12.2.3 单星号(*)可变参数
Python中函数的参数个数可以变化,它可以接受不确定数量的参数,这种参数称为可变函数。Python中可变参数有两种,即参数前加单星号(*)或双星号(**)形式。
单星号(*)可变参数在函数中被组装成为一个元组。
def sum(*numbers, multiple=1):
total = 0.0
for number in numbers:
total += number
return total * multiple
print(sum(100.0, 20.0, 30.0)) # 输出150.0
print(sum(30.0, 80.0)) # 输出110.0
print(sum(30.0, 80.0, multiple=2)) # 输出220.0
double_tuple = (50.0, 60.0, 0.0) # 元组或列表
print(sum(30.0, 80.0, *double_tuple)) # 输出220.0
在元组double_tuple前面加上单星号“*”,单星号在这里表示将doble_tuple拆包为50.0,60.0,0.0形式。另外,double_tuple也可以是列表对象。
12.2.4 双星号(**)可变参数
双星号(**)可变参数在函数中被组装成一个字典。
def show_info(sep=':', **info):
print('-----info-----')
for key, value in info.items():
print('{0} {2} {1}'.format(key, value, sep))
show_info('->',name='Tony', age=18, sex=True)
show_info(student_name='Tony', student_no='1000',sep='-')
stu_dict = {'name':'Tony', 'age':18} #创建字典对象
show_info(**stu_dict, sex=True, sep='=') #传递字典stud_dict
12.3 返回值
Python返回值主要有三种形式:无返回值、单一返回值和多返回值。
12.3.1 无返回值
有的函数只是为了处理某个过程,此时可以将函数设计为无返回值的。无返回值,事实上是返回None,None表示没有实际意义的数据。
def show_info(sep=':', **info):
"""定义**可变参数函数"""
print('-----info-----')
for key, value in info.items():
print('{0} {2} {1}'.format(key, value, sep))
return # return None 或省略
result = show_info('->', name='Tony', age=18, sex=True)
print(result) # 输出None
def sum(*numbers, multiple=1):
"""定义*可变参数函数"""
if len(numbers) == 0:
return # return None 或省略
total = 0.0
for number in numbers:
total += number
return total * multiple
print(sum(30.0, 80.0)) # 输出110.0
print(sum(multiple=2)) # 输出 None
12.3.2 多返回值函数
最简单的方式是使用元组返回多个值,因为元组可以作为数据结构可以容纳多个数据,另外元组是不可变的,使用起来比较安全。
def position(dt, speed):
posx = speed[0] * dt
posy = speed[1] * dt
return (posx, posy)
move = position(60.0, (10, -5))
print("物体位移:({0} {1})".format(move[0], move[1]))
传递时间是60.0s,速度是(10,-5)
12.4 函数变量作用域
全局变量:变量在模块中创建,作用域是整个模块。
局部变量:变量在函数中创建,作用域是整个函数。
# 创建全局变量x
x = 20
def print_value():
print("函数中x = {0}".format(x))
print_value()
print("全局变量x = {0}".format(x))
修改上述代码。
# 创建全局变量x
x = 20
def print_value():
# 创建局部变量x
x = 10
print("函数中x = {0}".format(x))
print_value()
print("全局变量x = {0}".format(x))
在print_value()函数中添加了x=10。函数中的x变量与全局变量x命名相同,在函数作用域内会屏蔽全局x变量。
提示:Python中创建的变量默认作用域是当前函数。
但是Python提供了一个global关键字,可将函数的局部变量作用域变成全局的。
# 创建全局变量x
x = 20
def print_value():
global x
x = 10
print("函数中x = {0}".format(x))
print_value()
print("全局变量x = {0}".format(x))
12.5 生成器
在函数中经常使用return关键字返回数据,但是有时会使用yeild关键字返回数据,返回的是一个生成器(generator)对象,生成器对象是一种可迭代对象。
例如计算平方数列,通常的实现代码。
def square(num):
n_list = []
for i in range(1, num + 1):
n_list.append(i * i)
return n_list
for i in square(5):
print(i, end=' ')
Python还有更好的解决方案。
def square(num):
for i in range(1, num + 1):
yield i * i
for i in square(5):
print(i, end=' ')
使用了yield关键字返回平方数,不再需要return关键字。
第二个for调用了函数square()返回的是生成器对象。生成器对象是一种可迭代对象,可迭代对象通过__next__()方法获得元素,for循环能够遍历可迭代对象,就是隐式地调用了生成器的__next__()方法获取元素的。
显式地调用生成器的__next__()方法:
return语句一次返回所有数据,函数调用结束;而yield语句只返回一个元素数据,函数调用不会结束,只是暂停,直到__next__()方法被调用,程序继续执行yield语句之后的语句代码。
提示:生成器特别适合用于遍历一些大序列对象,它无须将对象的所有元素都载入 内存后才开始进行操作,仅在迭代至某个元素时才会将该元素载入内存。
12.6 嵌套函数
嵌套函数:函数定义在另外的函数体中。
def caliculate(n1, n2, opr):
multiple = 2
# 定义相加函数
def add(a, b):
return (a + b) * multiple
# 定义相减函数
def sub(a, b):
return (a - b) * multiple
if opr == "+":
return add(n1, n2)
else:
return sub(n1, n2)
print(caliculate(10, 5, "+")) # 输出结果是30
# add(10, 5) 发生错误
# sub(10, 5) 发生错误
定义了两个嵌套函数add()和sub()。嵌套函数可以访问外部函数calculate()中的变量multiple,而外部函数不能访问嵌套函数局部变量。另外,嵌套函数的作用域在外部函数体内,因此外部函数体外直接访问嵌套函数会发生错误。
12.7 函数式编程基础
函数式编程(functional programming)与面向对象编程一样都 一种编程范式,函数式编程,也称为面向函数的编程。
12.7.1 高阶函数与函数类型
高阶函数:函数可以作为其他函数的参数,或者其他函数的返回值。
函数类型:任何一个函数的数据类型都是function类型。
def calculate_fun():
# 定义相加函数
def add(a, b):
return a + b
return add
f = calculate_fun()
print(type(f))
print("10 + 5 = {0}".format(f(10, 5)))
calculate_fun()函数的返回值是嵌套函数add,也可以说calculate_fun()函数的返回值数据类型是”函数类型“。
变量f指向add函数,变量f与函数一样可以被调用,f(10, 5)就是调用函数,也就是调用add(10,5)函数。
12.7.2 函数作为其他函数返回值使用
calculate_fun()函数是高阶函数,因为它可以把函数作为其他函数的返回值使用。
def calculate_fun(opr):
# 定义相加函数
def add(a, b):
return a + b
# 定义相减函数
def sub(a, b):
return a - b
if opr == "+":
return add
else:
return sub
f1 = calculate_fun("+")
f2 = calculate_fun("-")
print("10 + 5 = {0}".format(f1(10, 5)))
print("10 - 5 = {0}".format(f2(10, 5)))
12.7.3 函数作为函数参数使用
作为高阶函数还可以作为其他函数参数使用。
def calc(value, op): # op参数是一个函数
return op(value)
def square(n):
return n * n
def abs(n):
return n if n > 0 else -n
print("3的平方 = {}".format(calc(3, square)))
print("-20的绝对值 = {}".format(calc(-20, abs)))
calc(value, op)函数,value函数是要计算操作数,op参数是一个函数,可见该函数是一个高阶函数。
12.7.4 匿名函数与lambda表达式
匿名函数:在使用函数时不需要给函数分配一个名字。Python语言中使用lambda表达式表示匿名函数,声明lambda表达式语法如下:
lambda 参数列表 : lambda体
lambda是关键字声明,参数列表与函数的参数列表是一样的,但不需要小括号括起来,冒号后面是”lambda体“,lambda表达式的主要代码在此处编写,类似于函数体。
注意:lambda体部分不能是一个代码块,不能包含多条语句,只能有一条语句,语句会计算一个结果返回给lambda表达式,但是与函数不同的是,不需要使用return语句返回。与其它语言中的lambda表达式相比,Python提供的lambda表达式只能进行一些简单的计算。
def calculate_fun(opr):
if opr == "+":
return lambda a, b: (a + b)
else:
return lambda a, b: (a - b)
f1 = calculate_fun("+")
f2 = calculate_fun("-")
print(type(f1))
print("10 + 5 = {0}".format(f1(10, 5)))
print("10 - 5 = {0}".format(f2(10, 5)))
代替了add()和sub()函数。
12.8 函数时编程的三大基础函数
函数编程的本质是通过函数处理数据,过滤filter()、映射map()和聚合reduce()是处理数据的三大基本操作。
12.8.1 过滤函数filter()
filter()函数语法:
filter(function, iterable)
参数function是一个函数,参数iterable是可迭代对象。filter()函数调用时Iterable会遍历,它的元素被逐一传入function函数,function函数返回布尔值。在function函数中编写过滤条件,如果为True的元素被保留,如果为False的元素被过滤掉。
users = ['Tony', 'Tom', 'Ben', 'Alex']
users_filter = filter(lambda u: u.startswith('T'), users)
print(list(users_filter))
调用filter()函数过滤users列表,过滤条件是T开头的元素,lambda u: u.startswith('T')是一个lambda表达式,它提供了过滤条件。filter()函数还不是一个列表,需要使用list()函数转换过滤之后的数据成列表。
下一个示例:获取1~10中的偶数。
number_list = range(1, 11)
number_filter = filter(lambda it: it % 2 == 0,number_list)
print(list(number_filter))
12.8.2 映射函数map()
映射操作使用map()函数,可以对可迭代的对象的元素进行交换。
map()函数语法:
map(function, iterable)
参数function是一个函数,参数iterable是可迭代对象。map()函数调用时Iterable会遍历,它的元素被逐一传入function函数,在function函数中对元素进行变换。
users = ['Tony', 'Tom', 'Ben', 'Alex']
users_map = map(lambda u: u.lower(), users)
print(list(users_map))
函数式编程时数据可以从一个函数“流”入另一个函数,但Python不支持“链式”API。
例如:想获取users列表中T开头的名字,再将其转换为小写字母,这样的需求需要使用filter()函数进行过滤,再使用map()函数进行映射变换。
users = ['Tony', 'Tom', 'Ben', 'Alex']
users_filter = filter(lambda u: u.startswith("T"),users)
users_map = map(lambda u: u.lower(), filter(lambda u: u.startswith("T"),users))
# users_map = map(lambda u: u.lower(), users_filter) # 与上面代码相同的功能
print(list(users_map))
12.8.3 聚合函数reduce()
聚合操作将多个数据聚合起来输出单个数据,聚合操作中最基础的是聚合函数eduece(),reduece()函数会将多个数据按照指定的算法积累叠加起来,最后输出一个数据。
reduce()函数语法:
reduce(function, iterable[, initialiazer])
参数function是聚合操作函数,有两个参数,一个是参数iterable可迭代对象,另一个是initializer是初始值。
示例对一个数列的求和运算。
from functools import reduce
users = ['Tony', 'Tom', 'Ben', 'Alex']
a = (1, 2, 3, 4)
a_reduce = reduce(lambda acc, i: acc + i, a) # 10
print(a_reduce)
a_reduce = reduce(lambda acc, i: acc + i, a, 2) # 12
print(a_reduce)
reduce()函数实在funtools模块中定义的,所以要使用reduce()函数需要导入funtools模块。
lambda acc, i: acc + i, a是进行聚合操作的lambda表达式,lambda表达式有两个参数,acc参数是上次累加计算结果,i是当前元素,acc+i是进行累加。reduce()函数最后的计算结果是一个数值,可以直接通过reduce()函数返回。
12.9 装饰器
在不修改代码的情况下,给函数增加一些功能。
12.9.1 一个没有使用装饰器的示例
返回字符串的函数,在不修改函数的情况下,将返回的字符串转换为大写字符串。
def uppercase_decorator(func): # func是函数类型参数
def inner(): # 嵌套函数inner
s = func() # 调用func()函数并将返回值赋值给s变量
make_uppercase = s.upper() # s.upper()将字符串转换为大写字母并赋值给make_uppercase
return make_uppercase
return inner
def say_hello(): # 定义字符串返回函数say_hello()
return "hello world."
say_hello2 = uppercase_decorator(say_hello)
print(say_hello2())
12.9.2 使用装饰器
上面的代码麻烦,Python提供了装饰器注释功能。
def uppercase_decorator(func): # func是函数类型参数
def inner(): # 嵌套函数inner
s = func() # 调用func()函数并将返回值赋值给s变量
make_uppercase = s.upper() # s.upper()将字符串转换为大写字母并赋值给make_uppercase
return make_uppercase
return inner
@uppercase_decorator # 使用@uppercase_decorator装饰器声明say_hello()函数,装饰器本身是一种函数
def say_hello(): # 定义字符串返回函数say_hello()
return "hello world."
# say_hello2 = uppercase_decorator(say_hello) # 使用装饰器不需要显示调用uppercase_decorator()函数
print(say_hello()) # 而是直接调用say_hello()函数
12.9.3 同时使用多个装饰器
def uppercase_decorator(func): # func是函数类型参数
def inner(): # 嵌套函数inner
s = func() # 调用func()函数并将返回值赋值给s变量
make_uppercase = s.upper() # s.upper()将字符串转换为大写字母并赋值给make_uppercase
return make_uppercase
return inner
def bracket_decorator(func): # 给字符串添加括号
def inner():
s = func()
make_bracket = '[' + s + ']'
return make_bracket
return inner
@bracket_decorator
@uppercase_decorator # 使用@uppercase_decorator装饰器声明say_hello()函数,装饰器本身是一种函数
def say_hello(): # 定义字符串返回函数say_hello()
return 'hello world.'
# say_hello2 = uppercase_decorator(say_hello) # 使用装饰器不需要显示调用uppercase_decorator()函数
print(say_hello()) # 而是直接调用say_hello()函数
12.9.4 给装饰器传递参数
装饰器本质上是一个函数,因此可以给装饰器传递函数。
def calc(func):
def wrapper(arg1):
return func(arg1)
return wrapper
@calc
def square(n):
return n * n
@calc
def abs(n):
return n if n > 0 else -n
print("3的平方 = {}".format(square(3)))
print("-20的绝对值 = {}".format(abs(-20)))
装饰函数calc(func),它的参数还是一个函数。嵌套函数wrapper(arg1),这个函数参数列表与装饰器要注释的函数(如:square(n)和abs(n))参数列表一致。