【Python编程入门】Lecture 8:函数

8.1 函数简介

定义函数的一般格式为:
def 函数名():
(缩进)函数体

例如下面是一个打印问候语的函数。def是定义函数的关键字,该函数的函数名是greet_user()。函数体中第一行的文本注释称为文档字符串(docstring),它用来描述函数是做什么的,需要用三引号括起。print(“Hello!”)是函数体中唯一的一行代码。

def greet_user():
    """显示简单问候语"""
    print("Hello!")

要执行函数体中的代码,可以调用函数。调用函数时,指定函数名并加上圆括号即可。

greet_user()
Hello!

对于需要参数的函数,还需要在括号中指定参数,例如修改该函数以实现向指定的人打招呼。如果不指定参数,则程序会报错。

def greet_user(name):
    """向指定的人发送问候语"""
    print(f"Hello, {name}!")
greet_user('Alex')
Hello, Alex!

函数完成工作所需要的变量称为形参(形式参数)(parameter),调用函数是传递给函数的值称为实参(实际参数)argument)。在上例中,name是一个形参,'Alex’是一个实参。

8.2 传递实参的方式

函数定义中可能包含多个形参,因此调用函数时也需要指定多个实参。向函数传递实参的方式很多,以下主要介绍位置实参、关键字实参两种,此外还可以使用列表和字典传递实参。

8.2.1 位置实参

实参可以基于其位置顺序关联到函数定义中的形参,即实参的顺序与形参的顺序相同,这种关联方式称为位置实参

def friend_favorite(name, color):
    """朋友最喜欢的颜色"""
    print(f"My friend {name}'s favorite color is {color}!")
friend_favorite('Alex','blue')

注意实参的顺序很重要,如果顺序反了,会导致程序报错或产生逻辑错误。

friend_favorite('red','Alice')
My friend red's favorite color is Alice!

8.2.2 关键字实参

可以传递给函数名称值对,即每个实参都由变量名和值组成,这种关联方式称为关键字实参。关键字实参不仅无需考虑函数调用中实参的顺序,而且清楚地指出了函数调用中各个值的用途。

def friend_favorite(name, color):
    """朋友最喜欢的颜色"""
    print(f"My friend {name}'s favorite color is {color}!")
friend_favorite(color='red',name='Alice')
friend_favorite(name='Alice',color='red')
friend_favorite('Alice','red')
My friend Alice's favorite color is red!
My friend Alice's favorite color is red!
My friend Alice's favorite color is red!

由此可见,以上三种调用函数的方式是等效的。

8.2.3 默认值

编写函数时,可给每个形参指定默认值。在调用函数中给形参提供了实参时,Python将使用指定的实参值;否则,将使用形参的默认值。因此,给形参指定默认值后,可在函数调用中法省略相应的实参。使用默认值可简化函数调用,还可以清楚地指出函数的典型用法。

def friend_favorite(name, color='red'):
    """朋友最喜欢的颜色"""
    print(f"My friend {name}'s favorite color is {color}!")
friend_favorite('Alice')
friend_favorite('Bob','blue')
My friend Alice's favorite color is red!
My friend Bob's favorite color is blue!

注意,使用默认值时,必须将有默认值的形参放在没有默认值的形参之后,否则Python不能正确解读位置实参,会报错。

SyntaxError: non-default argument follows default argument

通过使用默认值,可以让调用函数时实参变为可选的。

def get_formatted_name(first_name, last_name, middle_name=''):
    '''返回整洁的姓名'''
    if middle_name:
        full_name = f"{first_name} {middle_name} {last_name}"
    else:
        full_name = f"{first_name} {last_name}"
    print(full_name.title())  
get_formatted_name('Rick', 'Durrett')
get_formatted_name('Rick', 'Durrett', 'lee')
Rick Durrett
Rick Lee Durrett

8.3 返回值

8.3.1 返回简单值

函数返回的值称为返回值
在函数中,可以使用return语句将值返回到调用函数的代码行。

def get_formatted_name(first_name, last_name, middle_name=''):
    '''返回整洁的姓名'''
    if middle_name:
        full_name = f"{first_name} {middle_name} {last_name}"
    else:
        full_name = f"{first_name} {last_name}"
    return full_name.title()
prof = get_formatted_name("rick","durret") 
print(prof)
Rick Durret

8.3.2 返回字典

函数可以返回任何类型的值,包括列表和字典等复杂数据结构。

def person_info(first_name, last_name, age=None):
    '''返回包含个人信息的字典'''
    person = {'first':first_name, 'last':last_name}
    if age:
        person['age'] = age
    return person
prof = person_info("rick","durret",age=100) 
print(prof)
{'first': 'rick', 'last': 'durret', 'age': 100}

可选形参age的默认值是None,它表示没有值,可以视为占位值。在条件测试中,None相当于False。

8.4 传递列表

将列表传递给函数后,函数就能直接访问其内容。

8.4.1 在函数中修改列表

假设完成用户提交的任务,将用户提交的所有任务名存储在一个列表中,完成后将任务名移动到另一个列表中,最后输出所有的已完成任务。为此,可以编写两个函数,一个负责处理打印任务,一个负责概述完成了哪些任务。

def tasks(unfinished,finished):
    '''依次完成任务直到没有未完成任务为止,并将已完成的任务一定到另一个列表中'''
    while unfinished:
        current_task = unfinished.pop()
        print(f"Current task:{current_task}")
        finished.append(current_task)
def show_finished_tasks(finished):
    '''显示所有已经完成的任务'''
    print("\nFinished tasks:")
    for task in finished:
        print(task)
unfinished = ['cooking','washing clothes','reading books']
finished = []
tasks(unfinished,finished)
show_finished_tasks(finished)
Current task:reading books
Current task:washing clothes
Current task:cooking

Finished tasks:
reading books
washing clothes
cooking

8.4.2 禁止函数修改列表

例如在上例中,需要将原始任务列表也保留,则需要禁止函数修改列表,为此,可以向函数传递列表的副本而非原件,可以采用切片表示法。

function_name(list_name[:])

虽然像函数传递列表的副本可以保留原始列表的内容,但除非有充分理由,否则还是应该将原始列表传递给函数。这是因为使用原始列表可以避免花费时间和内存创建副本,从而提高效率,这一点在处理大型列表时需要尤其注意。

8.5 传递任意数量实参

如果预先不知道需要接受多少个实参,可以使用星号*将收到的所有值封装到一个元组中。注意,即便函数只收到一个值也会封装到元组中。

def order_wine(*kinds):
    '''打印顾客点的所有配料'''
    print(kinds)
order_wine("Whiskey")
order_wine("Whiskey","Rum","Gin")
('Whiskey',)
('Whiskey', 'Rum', 'Gin')

注意,实践中可能会见到通用形参名*args。

8.5.1 结合使用位置实参和任意数量实参

如果要让函数接受不同类型的实参,必须在函数的定义中将接纳任意数量实参的形参放在最后。

def order_wine(number,*kinds):
    '''打印顾客点的所有配料'''
    print(f"{number}号桌点单:")
    for kind in kinds:
        print(f"-{kind}")
order_wine(3,"Whiskey")
order_wine(2,"Whiskey","Rum","Gin")
3号桌点单:
-Whiskey
2号桌点单:
-Whiskey
-Rum
-Gin

8.5.2 使用任意数量的关键字实参

如果预先不知道传递给函数的任意数量的实参是什么样的信息,可以使用两个星号**将收到的所有键值对封装到一个字典里。

def build_profile(first,last,**user_info):
    """"创建一个字典,存储有关用户的一切"""
    user_info["first_name"] = first
    user_info["last_name"] = last
    return user_info
user1 = build_profile("Wu","Kris",age = 18, length = 0.01)
print(user1)
{'age': 18, 'length': 0.01, 'first_name': 'Wu', 'last_name': 'Kris'}

注意,实践中可能会见到形参名**kwargs。

8.6 函数模块化

使用函数可以将代码块与主程序分离,使得主程序更加清晰易懂。更进一步地,可以将函数存储在称为模块的独立文件中,再将模块导入到主程序中。

8.6.1 创建模块

模块是扩展名为.py的文件,包含要导入到程序中的代码。例如将上例中的多于代码删除,只保留函数,并将文件存储为build.py,则可称为一个模块。

def build_profile(first,last,**user_info):
    """"创建一个字典,存储有关用户的一切"""
    user_info["first_name"] = first
    user_info["last_name"] = last
    return user_info

8.6.2 导入整个模块

在build.py所在目录中创建新的.py文件,在新的.py文件中,可以使用import语句导入模块,进而在当前程序中使用模块中的代码。

import build

上述代码让Python打开文件build.py,并在幕后将其中所有的函数都复制到了当前程序中,此时可以在当前程序中使用build.py中的所有函数。要调用build.py中的函数,需要采用句点表示法,即“模块名称.函数名称()”的格式(指定模块名称和函数名称并用句点分隔)。

build.build_profile("Wu","Kris",age = 18, length = 0.01)

如果导入的模块名字太长,可以使用关键字as给它指定别名。

import build as b
b.build_profile("Wu","Kris",age = 18, length = 0.01)

总结:

import module_name as mn
mn.function_name()

8.6.3 导入模块中的特定函数

导入模块中的特定函数可以采用如下语法:

from build import build_profile

注意,如果有多个函数,可以用逗号分隔。

from module_name import function_1, function_2

此时可以直接调用导入的函数。

build_profile("Wu","Kris",age = 18, length = 0.01)

如果导入的函数与现有的函数重名或名字太长,则需要使用关键字as给它指定独一无二的别名。

from build import build_profile as bp
bp("Wu","Kris",age = 18, length = 0.01)

总结:

from moudle_name import function_name as fn
fn()

注意,这等价于(不常用):

import moudle_name.function_name as fn
fn()

8.6.4 导入模块中的所有函数

使用星号(*)运算符可以导入模块中的所有函数,但不建议这么使用,此处列出仅做了解。

from module_name import *

由于导入了每个函数,因此在调用时可以直接使用名称来调用每个函数而无需使用句点表示法。但是,在使用他人编写的大型模块时,最好不要采用这个方法导入,因为如果被导入的模块中有函数与当前项目中的函数重名,会直接覆盖掉当前项目中的函数。

8.7 函数编写指南

1、每个函数都应该包含简要地阐述其功能的注释,并且注释应该采用文档字符串格式紧跟在函数定义后面。
2、给形参指定默认值时,等号两边不要有空格。对于函数调用中的关键字实参,等号两边也不要有空格。
3、PEP8建议代码行的长度不要超过79个字符,如果函数形参很多导致函数定义的长度超过了79个字符,可以在函数定义中输入左括号后按回车键(Enter),并按两次跳格键(Tab),从而将形参列表与函数体区分开(函数体只缩进了一层)。
4、所有import语句都应该放在文件开头,除非文件开头使用了注释来描述整个程序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值