第12章 函数与函数式编程

 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))参数列表一致。 

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值