在很多情况下,我们要实现功能需要编写很多重复或者相似的代码,它们的运行步骤基本相同,只是某些数据可能存在差异。这个时候,为了避免反复编写相同的代码,我们可以使用一个函数来对某段代码块进行封装,而在需要该代码块的地方,只需要调用函数即可。
定义函数
下面是一个非常简单的函数:
def showText():
print('Hello!')
showText()
这是一个最简单的函数结构,第一行使用def关键字来告知Python,这是一个函数,然后showText用于指定你的函数名,名字任意,括号内可以传递该函数所需的参数,因为仅仅是一个简单的输出语句,所以括号里并没有内容。最后,定义以冒号结尾。
要使用这个函数,我们只需要编写showText()即可,运行程序输出Hello!。
向函数传递信息
上述的代码段并不友好,接下来我们进行一个简单的改进:
def showText(name):
print('Hello!' + name)
showText('Jack')
只需稍作修改,就可以让showText()函数不仅向用户显示Hello!,还将用户的姓名用作抬头,为此,我们在函数的括号内添加name,表示在调用该函数的时候需要传递一个参数name,所以,我们在调用的时候,只需将用户的姓名传入,函数就会得到姓名并作相应的处理。
实参和形参
大多数编程语言中都有实参和形参的概念,在上述的程序段中,函数括号内的name即是形参,而在调用该函数时传入的‘Jack’即是实参。
传递实参
了解了实参和形参的概念后,我们来了解一下如何向函数传递实参。
传递实参的方式很多,我们上面的例子使用的是位置实参,那么什么是位置实参?位置实参要求实参的顺序与形参的顺序相同。也就是说,形参的参数位置是怎样的,你再传递实参的时候,参数位置也就应该是这样,因为在位置实参中,实参和形参是一一对应的。
def showText(name, age):
print(str(name) + '今年' + str(age) + "岁了")
showText('Jack', 20)
上述程序段中,我们只需传递姓名和年龄,程序就会输出Jack今年20岁了,但是,我们如果这样编写:
def showText(name, age):
print(str(name) + '今年' + str(age) + "岁了")
showText(20,'Jack')
这时候运行程序,输出20今年Jack岁了,很明显这样就闹笑话了。
所以,在使用位置实参方式时,千万要注意形参的顺序。
那么第二种传递实参的方式就是关键字实参。它要传递给函数的名称——值对,直接在实参中将名称和值进行一一对应,因此以这种方式传递实参就不会混淆,关键字实参让你无需考虑函数调用中的实参顺序,还清楚地指出了函数调用中各个值的用途。
def showText(name, age):
print(str(name) + '今年' + str(age) + "岁了")
showText(age=20, name='Jack')
上述程序段中,即使我们将实参顺序写错了,程序依然会输出正确的结果。但是,在使用关键字实参时,务必准确指定函数定义中的形参名。
默认值
编写函数时,可以给每个形参指定默认值,这样当你在调用函数的时候,如果没有传递实参,Python将会使用形参中的默认值进行处理。
def showText(name, age=20):
print(str(name) + '今年' + str(age) + "岁了")
showText('Jack')
上述程序段中,我们将age变量默认值指定为20,然后通过位置实参方式传递了Jack,程序运行正常。但是请注意,位置实参是严格要求实参位置的,所以如果你的形参name变量不是在第一位而是在第二位,程序就会报错,因为按照位置实参的规则,你的Jack是传递到第一个形参变量。所以,要么将name指定为第一个形参变量,要么就是用关键字实参方式传递实参。
返回值
函数并非总是直接输出,相反,它可以处理一些数据,并返回一个或一组值。函数返回的值被称为返回值,在函数中,可以使用return语句将值返回到调用函数的代码行。
下面来看一个函数:
def dealName(first_name, last_name):
full_name = first_name + ' ' + last_name
return full_name.title()
name = dealName('jimi', 'hendrix')
print(name)
该程序段将传递过去的姓氏和名字进行一个简单的拼接,并返回给调用者,进行输出。
有时候,需要让实参变成可选的,这样使用函数的人就只需在必要时才提供额外的信息。可使用默认值来让实参变成可选的。
def dealName(first_name, middle_name, last_name):
full_name = first_name + ' ' + middle_name + ' ' + last_name
return full_name.title()
name = dealName('john', 'lee', 'hooker')
print(name)
例如这样的一个程序段,当我们同时提供姓氏、名字和中间名时,程序正常运行。
然而并不是所有人都是有中间名的,但如果你只传入姓氏和名字的话,显然程序会报错,那该怎么办呢?我们可以让中间名变得可选,也就是给形参middle_name指定一个默认值——空字符串,并且在用户没有提供中间名的时候不使用这个形参。注意:我们需将形参middle_name移到参数末尾。
def dealName(first_name, last_name, middle_name=''):
if middle_name:
full_name = first_name + ' ' + middle_name + ' ' + last_name
else:
full_name = first_name + ' ' + last_name
return full_name.title()
name = dealName('john', 'hooker', 'lee')
print(name)
在这个示例中,姓名是根据三个可能提供的部分创建的。由于人都有名和姓,因此在函数定义中首先列出了这两个形参。中间名是可选的,因此在函数定义中最后列出该形参,并将其默认值设置为空字符串。
在函数体中,我们检查是否提供了中间名。Python将非空字符串解读为True ,因此如果函数调用中提供了中间名,if middle_name 将为True 。如果提供了中间名,就将名、中间名和姓合并为姓名,然后将其修改为首字母大写格式,并返回到函数调用行。在函数调用行,将返回的值存储在变量name 中;然后将这个变量的值打印出来。如果没有提供中间名,middle_name 将为空字符串,导致if 测试未通过,进而执行else 代码块:只使用名和姓来生成姓名,并将设置好格式的姓名返回给函数调用行。在函数调用行,将返回的值存储在变量musician 中;然后将这个变量的值打印出来。
返回字典
函数可返回任何类型的值,包括列表和字典等复杂的数据类型。
def create_person(name, age):
person = {
'name': name, 'age': age}
return person
person = create_person('张三', 20)
print(person)
上述程序段通过传递姓名和年龄从而构建出一个描述人信息的字典并返回。
可将函数同之前的任何Python结构结合起来使用,例如:
def get_formatted_name(first_name, last_name):
"""返回整洁的姓名"""
full_name = first_name + ' ' + last_name
return full_name.title()
# 这是一个无限循环!
while True:
print("\n请输入你的名字:")
f_name = input("姓: ")
l_name = input("名: ")
formatted_name = get_formatted_name(f_name, l_name)
print("\nHello, " + formatted_name + "!")
上述程序段通过while循环结果函数的用法,以用户输入的姓名进行友好交互。
传递列表
很多时候,你会发现,向函数传递列表很有用。将列表传递给函数后,函数就能直接访问其内容。
假设有一个用户的列表,我们要问候其中的每位用户:
def greet_users(users):
for name in users:
print('Hello!' + name.title())
users = ['张三', '李四', '王五']
greet_users(users)
输出完全符合预期,每位用户都看到了一条个性化的问候语。每当你要问候一组用户时,都可调用这个函数。
在函数中修改列表
将列表传递给函数后,函数就可对其进行修改。在函数中对这个列表所做的任何操作都是永久性的。
def deal_list(fruits):
fruits.pop()
fruits = ['apple', 'banana', 'pear', 'orange']
deal_list(fruits)
print(fruits)
禁止函数修改列表
有时候,需要禁止函数修改列表,这个时候,我们可以将列表的副本进行传递而不是传递原列表。
def deal_list(fruits):
fruits.pop()
return fruits
fruits = ['apple', 'banana', 'pear', 'orange']
fruits_new = deal_list(fruits[:])
print(fruits)
print(fruits_new)
传递任意数量的实参
有时候,你预先不知道函数需要接受多少个实参,好在Python允许函数从调用语句中收集任意数量的实参。
我们先来看一个例子:
def deal_hobby(*hobby):
print(hobby)
deal_hobby('足球', '篮球', '羽毛球', '乒乓球')
一个人的爱好是不确定数量的,所以在这里,我们将函数的形参定义为*hobby,星号让Python创建了一个名为hobby的空元组,并将接收到的值都封装到这个元组中。
结合使用位置实参和任意数量实参
如果要让函数接受不同类型的实参,必须在函数定义中将接纳任意数量实参的形参放在最后。Python先匹配位置实参和关键字实参,再将余下的实参都收集到最后一个形参中。
例如刚才的例子中,如果还需要一个表示姓名的形参,必须将该形参放在*hobby的前面。
def deal_hobby(name, *hobby):
print(name + '的爱好:' + str(hobby))
deal_hobby('张三', '足球', '篮球', '乒乓球')
上述程序段中,第一个实参张三会去匹配形参name,其余的三个实参都将存入形参hobby中。
使用任意数量的关键字实参
这个相信我不介绍你都会了吧,直接贴出示例代码。
def deal_hobby(name, **hobby):
print(name + '的爱好:' + str(hobby))
deal_hobby(name='张三', ball='篮球', sport=' 跑步')
将函数存储在模块中
函数的优点之一是,使用它们可将代码块与主程序分离。通过给函数指定描述性名称,可让主程序容易理解得多。你还可以更进一步,将函数存储在被称为模块 的独立文件中,再将模块导入 到主程序中。import 语句允许在当前运行的程序文件中使用模块中的代码。通过将函数存储在独立的文件中,可隐藏程序代码的细节,将重点放在程序的高层逻辑上。这还能让你在众多不同的程序中重用函数。将函数存储在独立文件中后,可与其他程序员共享这些文件而不是整个程序。知道如何导入函数还能让你使用其他程序员编写的函数库。
导入模块的方法有多种,下面对每种都作简要的介绍。
要让函数是可导入的,得先创建模块。模块 是扩展名为.py的文件,包含要导入到程序中的代码。
先创建demo9.py文件。
def sum(numbers):
sum = 0
for number in numbers:
sum += number
return sum
再创建demo10.py,通过import导入demo9模块。
import demo9;
numbers = (1, 2, 3, 4, 5, 6, 7, 8, 9)
sum = demo9.sum(numbers)
print(sum)
这样就能调用模块中的函数,从而计算出1~9之和。
导入特定的函数
你还可以导入模块中的特定函数,这种导入方法的语法如下:
from demo9 import sum
numbers = (1, 2, 3, 4, 5, 6, 7, 8, 9)
sum = sum(numbers)
print(sum)
在sum后面还可以跟上任意数量的函数名,中间用逗号分隔。
若使用这种语法,调用函数时就无需使用句点。由于我们在import 语句中显式地导入了函数sum() ,因此调用它时只需指定其名称。
使用as给函数指定别名
如果要导入的函数的名称可能与程序中现有的名称冲突,或者函数的名称太长,可指定简短而独一无二的别名 ——函数的另一个名称,类似于外号。要给函数指定这种特殊外号,需要在导入它时这样做。
from demo9 import sum as s;
numbers = (1, 2, 3, 4, 5, 6, 7, 8, 9)
sum = s(numbers)
print(sum)
上述程序段中,我们对demo9模块中的sum()函数起了一个别名s,这样我们在使用该函数的时候只需要写成s()即可。
使用as给模块指定别名
你还可以给模块指定别名。通过给模块指定简短的别名(如给模块pizza 指定别名p ),让你能够更轻松地调用模块中的函数。
import demo9 as d
numbers = (1, 2, 3, 4, 5, 6, 7, 8, 9)
sum = d.sum(numbers)
print(sum)
这样我们就能够通过别名d来调用模块中的函数了。
导入模块中的所有函数
如果想一次性导入模块中的所有函数,你可以这么做。
from demo9 import *
print(sum((1, 2, 3, 4, 5)))
使用星号可以让Python将模块中的所有函数进行导入,由于导入了每个函数,可通过名称来调用每个函数,而无需使用句点表示法。然而,使用并非自己编写的大型模块时,最好不要采用这种导入方法:如果模块中有函数的名称与你的项目中使用的名称相同,可能导致意想不到的结果:Python可能遇到多个名称相同的函数或变量,进而覆盖函数,而不是分别导入所有的函数。最佳的做法是,要么只导入你需要使用的函数,要么导入整个模块并使用句点表示法。这能让代码更清晰,更容易阅读和理解。这里之所以介绍这种导入方法,只是想让你在阅读别人编写的代码时,如果遇到类似情况,能够理解它们。