定义函数: def
def greet_user(): """显示简单的问候语""" print("Hello!") greet_user()
关键字def是函数定义:
在上面的代码中,函数名是greet_user(),它不需要任何信息就能完成工作,因此括号内是空的(但是括号不能少),最后定义以冒号结尾。
第二行的文本是文档字符串,描述了函数是做什么的,三个双引号能够包含多行。
向函数传递信息:
def greet_user(username): """显示简单的问候语""" print(f"Hello,{username.title()}!") greet_user("jim")
形参和实参:
在上面的代码中,username是一个形参,即函数完成工作所需的信息
greet_user("jim")中的"jim"是实参,在这个示例中,我们将实参“jim”赋给了形参username
练习8.2
def favorite_book(book_name): print(f"One of my favorite book is {book_name.title()}") favorite_book("daling")
传递实参:
①位置实参
在调用函数时,Python必须将函数调用中的每个实参关联倒函数定义中的一个形参,最简单的方式是基于实参的顺序进行关联,以这种方式关联的实参称为位置实参。
def describe_pet(animal_type,pet_name): print(f"I have a {animal_type}") print(f"{animal_type}'s name is {pet_name}") describe_pet('smoye','haha')
一个函数可以调用多次,在函数中,可根据需要使用任意数量的位置实参,Python将按顺序将函数调用中的实参关联到函数定义中相应的形参。
位置实参的顺序很重要!
②关键字实参
关键字实参是传递给函数的名值对,这样回直接在实参中将名称和值关联起来,因此向函数传递实参时就不会混淆了,关键字实参不仅让你无序考虑函数调用中的实参顺序,而且清楚地指出了函数调用中各个值的作用。
def describe_pet(animal_type,pet_name): """显示动物的信息""" print(f"I have a {animal_type}") print(f"{animal_type}'s name is {pet_name}") describe_pet(pet_name = 'hali', animal_type='hasik')
在使用关键字实参时,务必准确地指定函数定义中的形参名
③默认值
在编写函数时,可以给每个形参指定默认值。如果在调用函数中给形参提供了实参,Python将使用指定的实参值,否则,将使用形参的默认值。
def describe_pet(pet_name, animal_type='hasiki'): """显示动物的信息""" print(f"I have a {animal_type}") print(f"{animal_type}'s name is {pet_name}") describe_pet(pet_name = 'hali')
注意:当使用默认值时,必须在形参列表中先列出没有默认值的形参,再列出有默认值的形参,这让Python依然能够正确地解读位置实参
函数调用有很多形式,使用哪种调用方式无关紧要,只要函数调用能生成你期望的输出就好
对于
def describe_pet(pet_name, animal_type='dog')
下列的函数调用是等效的:
# 一条名为while的小狗 describe_pet('while') describe_pet(pet_name='while')
# 一只名为Harry的仓鼠 describe_pet('Harry','hamster') describe_pet(pet_name='harry', animal_type='hamster') describe_pet(animal_type='hamster',pet_name='harry')
如果再待用函数的时候少了实参,系统会报错
练习8.3:
def make_shirt(size,sentence): print(f"this is a {size} shirt with {sentence} on it")
练习8.4:
def make_shirt(size='big',sentence='I love Python'): print(f"this is a {size} shirt with {sentence} on it")
返回值
函数并非总是直接显示输出,它还可以处理一些数据,并返回一个或一组值,函数返回的值称为返回值:
def get_formatted_name(first_name,last_name): """返回标准格式的姓名""" full_name = f"{first_name} {last_name}" return full_name.title() musician = get_formatted_name('jimi','hendrix') print(musician)
让实参变成可选的:
有时候,需要让实参变成可选的,以便使用函数的人只在需要时才提供额外的信息。可以使用默认值来让实参变成可选的。
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() musician = get_formatted_name('jimi','hendrix') print(musician) musician = get_formatted_name('john','hooker','lee') print(musician)
可以选择是否有中间名,可选值让函数能够处理各种不同情形的同时,确保函数调用尽可能简单。
返回字典
函数可以返回任何类型的值,包括列表和字典等较为复杂的数据结构。
def build_person(first_name, last_name): """返回一个字典,其中包含有关一个人的名i在""" person = {'first': first_name, 'last': last_name} return person musician = build_person('jimi','hendrix') print(musician)
有选择性的输入年龄:
def build_person(first_name, last_name,age=None): """返回一个字典,其中包含有关一个人的名i在""" person = {'first': first_name, 'last': last_name} if age: person['age']=age return person musician = build_person('jimi','hendrix',27) print(musician)
其中None表示变量没有值,可以视为一个占位符
结合使用函数和while循环:
def get_formatted_name(first_name, last_name): """返回规范格式的名字""" full_name = f"{first_name} {last_name}" return full_name.title() while True: print("\nPlease tell me your name: ") print("enter 'q' at anytime to quit") f_name = input("First name:\n") if f_name == 'q': break l_name = input("Last name:\n") if l_name == 'q': break formatted_name = get_formatted_name(f_name,l_name) print(f"Hello, {formatted_name}")
练习8.6
def city_country(city_name,country_name): fact = f"{city_name},{country_name}" return fact situation = city_country('shanghai','China') print (situation)
练习8.7
def make_album(singer_nmae, album_name, song_number=None): albums = {'singer': singer_nmae, 'ablum': album_name} if song_number: albums['number'] = song_number return albums albums1 = make_album('zhoujielun', 'qilixiang',2) print(albums1)
练习8.8
def make_album(singer_nmae, album_name, song_number=None):
albums = {'singer': singer_nmae, 'ablum': album_name}
if song_number:
albums['number'] = song_number
return albums
print("任意时候输入'q'退出循环")
while True:
singer1 = input('请输入歌手的名字\n')
if singer1 == 'q':
break
albumname1 = input('请输入专辑的名字\n')
if albumname1 == 'q':
break
album1 = make_album(singer1, albumname1)
print(album1)
传递列表
def greet_users(names): """向列表中的每个用户发出简答的问候""" for name in names: msg = f"Hello,{name.title()}" print(msg) usernames = ['xiongjiajin', 'zhonghangyu', 'laijiawen'] greet_users(usernames)
在函数中修改列表
unprinted_designs = ['phone case', 'robot pendant', 'dodecahedron'] completed_models = [] # 模拟打印每个设计,知道没有未打印的设计为止 while unprinted_designs: current_design = unprinted_designs.pop() print(f"Printing model:{current_design}") completed_models.append(current_design) print("\nThe following models have been printed:") for completed_model in completed_models: print(completed_model)
也可以重新组织这些代码,编写两个函数,让每个都做一件具体的工作。大部分代码与原来相同,只是结构更为合理。第一个函数负责处理打印设计的工作,第二个概述打印了哪些设计:
def print_models(unprinted_designs, completed_designs): while unprinted_designs: current_design = unprinted_designs.pop() print(f"Printing model:{current_design}") completed_designs.append(current_design) def show_completed_models(completed_designs): print("The following models have been printed:") for completed_design in completed_designs: print(completed_design) unprinted_designs = ['phone case', 'robot pendant', 'ipad'] completed_designs = [] print_models(unprinted_designs,completed_designs) show_completed_models(completed_designs)
禁止列表修改函数:
可向函数传递列表的副本而不是原始列表
要将列表的副本传递给函数,可以像下面这样做:
function_name(list_name[:])
切片表示法[:]创建列表的副本,并不会影响列表本身
tip:虽然向函数传递列表的副本可保留原始列表的内容,但除非有充分的理由,否则还是应该将原始列表传递给函数,这是因为,让函数使用现成的列表可避免花时间和内存创建副本,从而提高效率,在处理大型列表时尤其如此。
练习8.9:
messages = ['what are you doing', 'what can I say', 'elephant in the room' ] def print_messages(messages_list): for message in messages_list: print(message) print_messages(messages)
练习8.10:
messages = ['what are you doing', 'what can I say', 'elephant in the room' ] sent_messages = [] def print_messages(messages_list): for message in messages_list: print(message) def send_messages(messages, sent_messages): while messages: current_message = messages.pop() sent_messages.append(current_message) print_messages(messages) send_messages(messages, sent_messages) print(messages) print(sent_messages)
练习8.11:
messages = ['what are you doing', 'what can I say', 'elephant in the room' ] sent_messages = [] def print_messages(messages_list): for message in messages_list: print(message) def send_messages(messages, sent_messages): while messages: current_message = messages.pop() sent_messages.append(current_message) print_messages(messages) sttend_messages(messages[:], sent_messages) print(messages) print(sent_messages)
传递任意数量的实参
有时候,你预先不知道函数需要接受多少个实参,好在Python允许函数从调用语句中手机任意数量的实参。
例如一个制作披萨的函数,它需要接受很多配料,但无法预先确定顾客要点多少种配料。下面的函数只有一个形参 *toppings,不管调用语句提供了多少实参,这个形参都会将其收入囊中。
def make_pizza(*toppings): print(toppings) make_pizza('pepperoni') make_pizza('mushroonms', 'green peppers', 'extra cheese')
还可以将函数调用print()替换为一个循环,遍历配料列表并对顾客点的披萨进行描述:
def make_pizza(*toppings): print("Making a pizza with the following toppings:") for topping in toppings: print(f"- {topping}") make_pizza('pepperoni') make_pizza('mushroonms', 'green peppers', 'extra cheese')
不管函数收到多少个实参,这种语法都管用
结合使用位置实参和任意数量的实参
如果要让函数接受不同类型的实参,必须在函数定义中将接纳如意数量实参的形参放在最后
例如:
def make_pizza(size,*toppings): print(f"Making a {size} inch pizza with the following toppings:") for topping in toppings: print(f"- {topping}") make_pizza(12,'pepperoni') make_pizza(18, 'green peppers', 'extra cheese')
基于上述函数定义,Python将收到的第一个值赋给形参size,将其他所有的值都存储在元组toppings中。
使用任意数量的关键字实参:
有时候,你需要接受任意数量的实参,但预先不知道传递给函数的是什么样的信息,在这种情况下,可将函数编写成能够接受任意数量的键值对——调用语句提供了多少就接受多少。
def build_profile(first, last, **user_info): user_info['first_name'] = first user_info['last_name'] = last return user_info user_profile = build_profile('xiong', 'jaijin', location = 'jiangxi', field = 'civil') print(user_profile)
tip:你经常会看到形参名**kwargs,它用于收集任意数量的关键字实参
练习8.12:
def make_pizza(*toppings): print("make your pizza with the following toppings:") for topping in toppings: print(f"-{topping}") make_pizza('mushrooms','tomatoes','potatoes') make_pizza('mushrooms') make_pizza('fish','beef')
练习8.13:
def build_profile(first, last, **user_info): user_info['first_name'] = first user_info['last_name'] = last return user_info user_profile = build_profile('xiong', 'jaijin', location = 'jiangxi', field = 'civil', hometown = 'ganzou') print(user_profile)
练习8.14:
def car_info(factory, stratage, **other_info): other_info['factory'] = factory other_info['stratage'] = stratage return other_info car1 = car_info('bmw', 'x4',price=14000, year=2009) print(car1)
函数定义中使用到的参数必须为形参
将函数存储在模块中:
使用函数的优点之一是将代码块与主程序分离,通过给函数指定描述性名称,能让程序容易理解得多,我们还可以更进一步,将函数存储在称为模块的独立文件中,再将模块导入主程序,import语句可以让你在当前运行的程序文件中使用模块中的代码。
导入整个模块
模块是拓展名为.py的文件,包含要导入程序的代码
例如:将make_pizza函数放在pizza.py文件当中,然后再pizza.py相同文件夹中创建另一个文件,引入pizza模块,再使用其中的函数
import pizza pizza.make_pizza(16,'mushroom') pizza.make_pizza(12,'mushrooms','green peppers','extra cheese')
使用模块中的公式,可采用以下公式:
module_name.function_name()
导入特定的函数
还可以只导入模块中的特定函数:
from module_name import function_name
用逗号分隔函数名,可以根据需要从模块中导入任意数量的函数
from module_name import function_0, function_1, function_2
例:
from pizza import make_pizza make_pizza(16, 'pepperoni') make_pizza(12,'mushroom', 'green peppers', 'extra cheese')
如果使用这种句法,在调用函数时则无需使用句点,由于在import语句中显式地导入了make_pizza()函数,因此在调用时只需指定其名称即可
使用as给函数指定别名
如果要导入的函数的名称太长或者可能与程序中既有的名称冲突,可指定简短而独一无二的别名:函数的另一个名称,类似于外号。
from pizza import make_pizza as mp mp(16, 'pepperoni') mp(12,'mushroom', 'green peppers', 'extra cheese')
上面的import语句将函数make_pizza()重命名为mp()。在这个程序中,每当需要调用make_pizza()时,都可将其简写成mp()。Python将运行make_pizza()中的代码,同时避免与程序可能包含的make_pizza()函数混淆
指定别名的通用语法如下:
from module_name import function_name as fn
使用as给模块指定别名
import pizza as p p.make_pizza(16,'mushroom') p.make_pizza(12,'mushrooms','green peppers','extra cheese')
这样让代码更加简洁,还让你不用再关注模块名,只专注于描述性的函数名
给模块指定别名的通用语法如下:
import module_name as mn
导入模块中的所有函数(谨慎使用)
使用(*)运算符可以让Python导入模块中的所有函数:
from pizza import * make_pizza(16, 'mushroom') make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
这种语法将模块pizza中的每个函数都复制到这个程序文件当中,由于导入了每个函数,可通过名称来调用每个函数,无需使用点号。
然而,在使用并非自己编写的大型模块时,最好不要使用这种导入方法,因为如果模块中有函数的名称与当前项目中既有的名称相同,可能导致意想不到的结果:Python可能会因为遇到多个名称相同的函数或变量而覆盖函数,而不是分别导入所有的函数。
最佳的做法是,要么只导入需要使用的函数,要么导入整个模块并使用点号。
函数编写指南:
①应给函数指定描述性名称,且只使用小写字母和下划线
②每个函数都应包含简要阐述其功能达到注释
③在给形参指定默认值时,等号两边不要有空格
④所有的import语句都应该放在文件开头,唯一例外是你要在文件开头使用注释来描述整个程序
⑤如果程序或模块包含多个函数,可使用两个空行将相邻的函数分开,这样将更容易知道前一个函数到什么地方结束,下一个函数从什么地方开始。