定义函数
函数是带有名称的代码块,用于完成具体的工作,通过调用函数可以执行其中定义的特定任务。如果需要在程序中多次执行同一项任务,这时候就无需反复编写完成该任务的代码,只要执行该任务的函数,让Python执行其中的代码即可。
函数的使用必须遵循 "先定义,后使用" 的原则。函数的定义就相当于事先将函数体代码保存起来,然后将内存地址赋值给函数名,函数名就是对这段代码的引用,这个变量的定义是相似的。简单的函数定义的语法如下:
def 函数名(参数1,参数2,...):
函数体
return 值
- def:该关键词用来告诉Python要定义一个函数
- 函数名:函数名指向函数内存地址,是对函数体代码的引用( 函数的命名应该反映出函数的功能 )
- 括号:括号内用来定义参数, 参数是可有可无的,且无需指定参数的类型
- 冒号:括号后面需要加上冒号,然后在下一行开始编写函数体的代码
- 函数体:由语句和表达式组成
- return值:定义函数的返回值, return是可有可无的
def say_hello():
'''
作者:FQYY
描述:定义了一个打印消息的函数
返回值:无
参数:无
'''
print("Hello World")
定义函数的时候发生的事情:
- 申请内存空间保存函数的代码
- 将上述的内存地址绑定到函数名
- 定义函数的时候不会执行函数体的代码,但是会检测函数体的语法
函数的调用使用 函数名() 的方式,只有在函数调用的时候才会执行函数体代码:
def say_hello():
'''
作者:FQYY
描述:定义了一个打印消息的函数
返回值:无
参数:无
'''
print("Hello World")
say_hello() # Hello World
调用函数的时候发生的事情:
- 通过函数名找到函数内存地址
- 加括号就是在触发函数体代码的执行
函数可以调用多次:
def say_hello():
'''
作者:FQYY
描述:定义了一个打印消息的函数
返回值:无
参数:无
'''
print("Hello World")
say_hello() # Hello World
say_hello() # Hello World
当函数体代码为 pass 的时候表示什么都不做,称之为空函数 。在程序设计的开始,往往是先想好程序都需要完成什么功能,然后将所有的功能都列举出来用pass充当函数体占位符。这使得程序的体系结构立见、代码清晰且可读性强。后期可以根据编程任务去选择性的实现上述功能来替换掉pass,从而提高开发的效率:
def connect():
'''
链接数据库
'''
pass
返回值
函数并不总是用来直接显示输出的,有时候也需要处理一些数据并返回一个或者多个值:
def get_message():
'''
返回一个字符串
'''
return "Hello World"
print(get_message()) # Hello World
当省略return语句或者return后面没有值的时候,函数的返回值是None:
def getMessage():
return
def sayHello():
pass
print(getMessage()) # None
print(sayHello()) # None
函数的返回值是没有类型限制的:
# 返回字符串
def returnString():
return "Hello World"
# 返回数字
def returnNumber():
return 12
# 返回列表
def returnList():
return [1, 2, 3, 4]
# 返回字典
def returnDic():
return {"name": "张三", "age": 12}
# 返回元祖
def returnTuple():
return (1, 2, 3)
# 或者
def returnValue():
return 1, 2, 3
return是一个函数结束的标志,函数内可以有多个return语句,但是只执行一次return语句就结束了:
def getMessage():
return "Hello World"
return "Hello Python"
print(getMessage()) # Hello World
传递参数
在定义函数的时候可以传递参数,函数的参数分为形参和实参:
- 形参:在定义函数的时候括号内声明的参数。形参本质就是一个变量名,用来接受外部传来的值
- 实参:调用函数时括号内传入的值,值可以是变量、常量、表达式或者三者的组合
def say_hello(username):
print("Hello {}".format(username))
say_hello("Jack")
在上面的案例中,username 就是一个形参,而在调用函数的时候传入的 Jack 就是一个实参,即在调用函数的时候将 Jack 传递给了 username 。
注意: 在调用有参数函数时,实参(值)会赋值给形参(变量名)。在Python中,变量名与值只是单纯的绑定关系,而对于函数来说这种绑定关系只有在调用函数的时候生效,函数调用结束后就解散。
在函数定义的时候可以包含多个形参,因此调用函数的时候也可以包含多个实参,向函数传递实参的方法很多,下面介绍的是几个常见的函数传递方法。
位置参数
位置参数是基于参数的顺序,要求实参的顺序和形参的顺序相同:
def greet_user(username, age):
print("My name is {}, I'm {} years old".format(username, age))
greet_user("Jack", 16)
greet_user("Tom", 18)
关键字参数
每个实参都是由变量名和值组成,因为直接在实参中将名称和值关联起来,所以向函数传递实参的时候不会发生混淆。关键字参数让你无需考虑函数调用中的实参的顺序,还清楚地指出了函数调用中各个值的用途:
def greet_user(username, age):
print("My name is {}, I'm {} years old".format(username, age))
greet_user(username="Jack", age=16)
greet_user(age=18, username="Tom")
使用关键字参数的时候需要注意下面几点:
- 使用关键字实参的时候,务必准确地指定函数定义的形参名
- 必须保证关键字参数在位置参数后面
- 不可以对一个形参重复赋值
def printInfo(username, age, gender):
print("name: {}, age: {}, gender: {}".format(username, age, gender))
printInfo(username="Jack", age=18, sex="man")
# TypeError: printInfo() got an unexpected keyword argument 'sex'
printInfo(username="Jack", 18, gender="man")
# SyntaxError: positional argument follows keyword argument
printInfo(username="Jack", age=18, gender="man", gender="woman")
# SyntaxError: keyword argument repeated
默认值
编写函数的时候可以给每个参数指定默认值,如果在调用函数的时候给这些形参指定了实参,Python将使用传递的实参值,否则将使用形参的默认值:
def greet_user(username, age=20):
print("My name is {}, I'm {} years old".format(username, age))
greet_user(username="Jack", age=19) # My name is Jack, I'm 19 years old
greet_user(username="Tom") # My name is Tom, I'm 20 years old
注意:有默认值的形参必须放在没有默认值的形参的后面。
可变长度的参数
有时候预先不知道要接受多少个实参,Python允许函数从调用语句中传递任意数量的实参。
可变长度的位置参数
如果在最后一个形参名前面加上 "*" ,那么在调用函数的时候,所有多出来的位置实参都会被该参数接收,并且会以元祖的形式保存下来( 即便只收到一个值 ,Python 也会将实参封装到一个元祖中 ):
def find_user(*user):
print(user)
find_user("Jack")
find_user("Jack", "Tom")
# ('Jack',)
# ('Jack', 'Tom')
如果想要让函数接受不同类型的参数,必须在函数定义中将可变长度的形参放在最后,Python会先匹配位置实惨和关键字实惨,再将余下的实参都收集到最后一个形参中:
def func_user(num1, *numbers):
print(num1)
print(numbers)
func_user(12,12,32,23)
# 12
# (12, 32, 23)
也可以将一个列表传递给可变参数:
def func_user(num1, *numbers):
print(num1)
print(numbers)
func_user(1, *[2, 3, 4])
# 1
# (2, 3, 4)
传入列表的时候在前面加上 "*" 的意思是将列表中的值进行解压,然后赋值给可变参数。如果这里不加就会将列表当作一个整体传递给可变参数:
def func_user(num1, *numbers):
print(num1)
print(numbers)
func_user(1, [2, 3, 4])
# 1
# ([2, 3, 4],)
可变长度的关键字实惨
如果在最后一个形参名前加 "**" ,那么在调用函数时,所有多出来的关键字参数都会被该形参接收,并且会以字典的形式保存下来:
def func_user(first_name, last_name, **user_info):
print(first_name)
print(last_name)
print(user_info)
func_user("Stephen", "Curry", local="American", age=32)
# Stephen
# Curry
# {'local': 'American', 'age': 32}
也可以将字典传递给可变参数:
def func_user(first_name, last_name, **user_info):
print(first_name)
print(last_name)
print(user_info)
info = {"local": "American", "age": 32}
func_user("Stephen", "Curry", **info)
# Stephen
# Curry
# {'local': 'American', 'age': 32}
如果在传入字典的时候没有在字典前面加上 "**" ,那么该字典就是只是一个普通的位置参数了,这个时候运行程序就会报错:
def func_user(first_name, last_name, **user_info):
print(first_name)
print(last_name)
print(user_info)
info = {"local": "American", "age": 32}
func_user("Stephen", "Curry", info)
# TypeError: func_user() takes 2 positional arguments but 3 were given
命名关键字参数
在定义了可变长度的关键字参数后就可以传入任意的关键字参数,但如果函数体代码的执行就需要依赖与某个key,此时就必须在函数内进行判断:
def func_user(first_name, last_name, **user_info):
if "gender" in user_info:
pass
如果想要限定函数的调用者必须以 key=value 的形式传入一些值,在Python3中可以在定义形参的时候使用一个 "*" 作为分隔符号,在这个 "*" 之后的形参称之为 命名关键字参数 。对于这类参数,在函数调用的时候必须按照 key=value 的形式为其传值,且必须得传值:
def printInfo(name, age, *, gender, salary):
pass
printInfo('lili', 18, gender='male', salary=12) # 正确使用
printInfo('lili', 18, 'male', 12)
# # TypeError: printInfo() takes 2 positional arguments but 4 were given
printInfo('lili', 18, gender='male')
# TypeError: printInfo() missing 1 required keyword-only argument: 'salary'
命名关键字参数也可以有默认值:
def printInfo(name, age, *, gender="male", salary):
pass
printInfo('lili', 18, salary=12)
注意: 这里的形参 gender='male' 属于命名关键字参数的默认值,因而即便是放到形参 salary 之前也不会有问题。
如果形参中已经创建了一个可变长度的参数,那么命名关键字参数就不再需要一个单独的 "*" 作为分隔符:
def printInfo(name, age, *info, gender="male", salary):
pass
printInfo('lili', 18, 1,salary=12)
组合参数
前面介绍的所有的参数都可以任意组合使用,但定义的顺序必须是: 位置参数、默认参数、*定义的可变参数、命名关键字参数、**定义的可变参数 :
def printInfo(name, age=20, *args, gender="male", **kwargs):
print(name, age, args, gender, kwargs)
printInfo("Jack", *[1,2,3], salary=20)
# Jack 1 (2, 3) male {'salary': 20}
注意: *args、**kwargs中的args和kwargs可以被替换成其它的名称,但使用args、kwargs是约定俗成的。
列表参数
在函数中是可以修改列表的,也就是说如果将列表传入到函数中,那么在函数中对列表的操作会影响到列表本身:
user = ["Tom", "Jack", "Clair"]
del_user = []
def func_del_user(old_user, new_user):
while old_user:
new_user.append(old_user.pop())
func_del_user(user, del_user)
print(user) # []
print(del_user) # ['Clair', 'Jack', 'Tom']
注意: python传递的是内存地址。
如果不想修改原列表,可以使用下面的方法:
user = ["Tom", "Jack", "Clair"]
del_user = []
def func_del_user(old_user, new_user):
while old_user:
new_user.append(old_user.pop())
func_del_user(user[:], del_user)
print(user) # ["Tom", "Jack", "Clair"]
print(del_user) # ['Clair', 'Jack', 'Tom']
函数类型提示
在Python3.5之后,可以对函数的参数和返回值设置类型提示:
def register(name:str, age:int=18)->int:
print(name)
return 1