函数是带名字的代码块,用于完成具体的工作。
函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
通过使用函数,程序的编写、阅读、测试和修复都将更容易。
使用def关键字定义一个函数,定义函数不会执行定义的代码,需要调用才会执行;
定义函数的规则:
- 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号()。
- 任何传入参数和自变量必须放在圆括号中间。圆括号之间可以用于定义参数。
- 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
- 函数内容以冒号起始,并且缩进。
- return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。
#定义一个简单的函数:
def greet_user(): #函数声明;定义函数名为greet_user
print("Hello!") #函数体
greet_user() #调用函数
#输出:
Hello!
只需稍作修改,就可以让上面的函数greet_user() 不仅向用户显示Hello! ,还将用户的名字用作抬头。为此,可在函数定义def greet_user() 的括号内添加username 。
通过在这里添加username ,就可让函数接受你给username 指定的任何值。
现在,这个函数要求你调用它时给username 指定一个值。调用greet_user() 时,可将一个名字传递给它,如下所示:
def greet_user(username):
"""显示简单的问候语,因为它是函数体需要缩进,否则会报错"""
print("Hello, " + username.title() + "!")
greet_user('sarah')
greet_user('lusy')
#输出:
Hello, Sarah!
Hello, Lusy!
调用函数时,Python必须将函数调用中的每个实参都关联到函数定义中的一个形参。
为此,最简单的关联方式是基于实参的顺序。这种关联方式被称为位置参数。
一个简单的示例,定义一个"def add(x,y)"函数,但是没有直接在函数名内直接写死入参,
这样每次调用的时候,就可以自由的给括号内的x和y进行传值
#案例1:
def add(x,y): #定义一个add函数,括号内x,y为形参
print(x + y)
add(123,4869) #调用的时候,动态传入实际参数
add(789,903)
#输出:
4992
1692
为什么不能直接写死参数,而要用形参的方式?
因为形参通过函数调用时动态传入实参,更加灵活。
比如:一个登录系统,有100个用户,如果直接在函数体内写死为用户A的账号和密码,那么输入其他用户的账号和密码,在登录进行后台验证的时候,都只能是用户A。
示例如下:
#案例登录功能
def login():
username = "wang"
pwd = "123"
print(f"{username}用户验证,密码为{pwd}")
login()
#输出:wang用户验证,密码为123
改为动态传参:
#改成如下:
def login(username,pwd):
print(f"{username}用户验证,密码为{pwd}")
login("wang","123") #动态传参
login("yuan","234")
#输出:
wang用户验证,密码为123
yuan用户验证,密码为234
函数可多次调用:
可以根据需要调用函数任意次。要再描述一个宠物,只需再次调用describe_pet() 即可:
#案例1:
def describe_pet(animal_type, pet_name):
"""显示宠物的信息"""
print("\nI have a " + animal_type + ".")
print("My " + animal_type + "'s name is " + pet_name.title() + ".")
describe_pet('hamster', 'harry')
describe_pet('dog', 'willie') #第二次调用
#输出:
I have a hamster.
My hamster's name is Harry.
I have a dog.
My dog's name is Willie.
再比如:
有一份20多行的打印菱形的代码,如果需要多次进行菱形打印,我们不可能去反复写代码,那么就可以把它封装为一个函数,每次直接进行调用就行,省时省力!~
#定义打印菱形的函数,菱形层数由rows决定
def pringling(rows):
i = j = k = 1
for i in range(rows):
for j in range(rows - i):
print(" ",end = " ")
j += 1
for k in range(2 * i -1):
print("*",end = " ")
k += 1
print("\n")
#菱形的下半部分:
for i in range(rows):
for j in range(i):
print(" ",end = " ")
j += 1
for k in range(2 * (rows - i) - 1):
print("*",end = " ")
k += 1
print("\n")
pringling(3) #调用的时候,根据需要给rows动态传参;3层菱形
print("do something……")
pringling(6) #再想打印一个6层菱形,再调用
#输出:
*
* * *
* * * * *
* * *
*
do something……
*
* * *
* * * * *
* * * * * * *
* * * * * * * * *
* * * * * * * * * * *
* * * * * * * * *
* * * * * * *
* * * * *
* * *
*
使用位置实参来调用函数时,参数值的位置需要和参数名位置对应
如果参数的顺序不正确,结果可能出乎意料:
比如,上面的describe_pet函数中,我们想要通过形参的方式,打印一只叫“harry”的仓鼠
但是调用的函数的时候,把名字放在了前面,输出效果如下:
def describe_pet(animal_type, pet_name):
print("\nI have a " + animal_type + ".")
print("My " + animal_type + "'s name is " + pet_name.title() + ".")
describe_pet('harry', 'hamster') #把仓鼠名字放在前,仓鼠放在后
#输出:
I have a harry.
My harry's name is Hamster.
#正确的传参:调用函数,与最上面定义的形参名称呼应,宠物类型在前,宠物名在后
describe_pet('hamster', 'harry')
#输出:
I have a hamster.
My hamster's name is Harry.
其次是,调用时传入的实参个数,需要与形参相匹配,多一个或少一个都会报错
错误示例如下:
def info(name,age):
print("name:",name)
print("age:", age)
info(19,"wang") #传入实参顺序与形参不匹配
#输出:
name: 19
age: wang
info("小红",30,"小明") #多写一个实参
#报错如下:
TypeError: info() takes 2 positional arguments but 3 were given
info(19) #少写一个实参
#报错如下:
TypeError: info() missing 1 required positional argument: 'age'
关键字实参是传递给函数的名称—值对。
直接在实参中将名称和值关联起来了,因此向函数传递实参时不会混淆(不会得到上述名为Hamster的harry这样的结果)。
关键字实参无需考虑函数调用中的实参顺序,还清楚地指出了函数调用中各个值的用途。
与形参不同的是:
调用时直接对参数进行 = 赋值
从此,妈妈再也不用担心,出现位置实参顺序与形参不匹配的情况了~
特别是当一个函数要多个不同的文件中调用时,或者是我们调用别人的包,我们不可能记得或清楚每个参数的位置,通过这种方式,可以简单高效且顺利的完成调用。
示例如下:
def describe_pet(animal_type, pet_name):
"""显示宠物的信息"""
print("\nI have a " + animal_type + ".")
print("My " + animal_type + "'s name is " + pet_name.title() + ".")
#括号内的两个值,也可以反过来写,因为直接对参数名做了赋值,所以位置顺序不受影响
describe_pet(animal_type ='hamster', pet_name ='harry')
#输出:
I have a hamster.
My hamster's name is Harry.
编写函数时,可给每个形参指定默认值 。
就是当某一个参数基本是固定的时候,我们不需要先定义形参,然后每次都重复传入同一个实参,偷懒的写法,就是直接给形参指定默认值。
使用默认值可简化函数调用,还可清楚地指出函数的典型用法。
例如:
调用describe_pet() 时,描述的基本都是是小狗,就可将形参animal_type 的默认值设置为'dog' 。这样,调用describe_pet() 来描述小狗时,每次就只需要传pet_name的实参即可:
# 定义函数时,直接给animal_type指定了默认值,无需再通过该实参来指定动物类型
def describe_pet(pet_name, animal_type='dog'):
"""显示宠物的信息"""
print("\nI have a " + animal_type + ".")
print("My " + animal_type + "'s name is " + pet_name.title() + ".")
describe_pet(pet_name='willie') #调用是就不用再传animal_type
#输出:
I have a dog.
My dog's name is Willie.
如果定义函数时,已经指定了默认值,但实际调用又需要更改实参,要怎么办呢?
只需要在调用时重新传入对应的实参,就相当对实参重复赋值。
示例如下:
# 定义函数时,直接给animal_type指定默认值为 = dog
def describe_pet(pet_name, animal_type='dog'):
"""显示宠物的信息"""
print("\nI have a " + animal_type + ".")
print("My " + animal_type + "'s name is " + pet_name.title() + ".")
#调用时把animal_type改为cat
describe_pet(pet_name ='willie', animal_type = 'cat')
#输出:
I have a cat.
My cat's name is Willie.
一个*表示不定长参数(不确定长度)
可以传任意长度的参数,可以是0个,也可以是多个
#示例1:
def add(*args):
print(type(args))
#返回传入参数的和:
sum = 0
for item in args:
sum += item
return sum
a = add(1,2,3,4,6)
print(a)
#输出:
16
#示例2:
a = add(1,2,3,4,6)
print(a)
print("*" *100)
def cal(caozuo,*nums):
if caozuo == "+":
print(nums)
s = 0
for i in nums:
s += i
print(s)
cal("+",3,88,73)
#输出:
(3, 88, 73)
164
2**kwargs也表示不定长参数,初始值是字典类型、传key和value
在调用函数时,多余的键值对都会写入形参**kwargs字典
如下所示:
● 初始定义3个形参和一个不定长参数
● 第一次调用时,第4个实参对应**kwargs
● 第二次调用时,第4个、第5个实参都对应**kwargs
def print_stu_info(name,age,gender,**kwargs):
print("name:",name)
print("age:", age)
print("gender:", gender)
print("kwargs:", kwargs)
print_stu_info(name = "张三",age = 22,gender = "male",weight = "70KG")
print("*" *100)
print_stu_info(name="李四", age=22, gender="male", weight="70KG",haight = 180)
#输出:
name: 张三
age: 22
gender: male
kwargs: {'weight': '70KG'}
****************************************************************************************************
name: 李四
age: 22
gender: male
kwargs: {'weight': '70KG', 'haight': 180}
鉴于可混合使用位置实参、关键字实参和默认值,通常有多种等效的函数调用方式。
例如下面的函数describe_pets() 的定义,其中给一个形参提供了默认值:
def describe_pet(pet_name, animal_type='dog'):
基于这种定义,在任何情况下都必须给pet_name 提供实参;
指定该实参时可以使用位置方式,也可以使用关键字方式。
如果要描述的动物不是小狗,还必须在函数调用中 给animal_type 提供实参;
同样,指定该实参时可以使用位置方式,也可以使用关键字方式。
下面对这个函数的所有调用都可行:
#一条名为Willie的小狗
describe_pet('willie')
describe_pet(pet_name='willie')
# 一只名为Harry的仓鼠
describe_pet('harry', 'hamster')
describe_pet(pet_name='harry', animal_type='hamster')
describe_pet(animal_type='hamster', pet_name='harry')
总结:
1. 综上,不论是哪种函数参数的形式,本质上来讲:
定义函数,就是定义一个变量、定义变量参数(形参相当于临时变量)
调用函数,就是对变量进行赋值(赋予实际变量值)
2. 四种参数同时出现,固定写法顺序排列:
位置参数、不定长参数、默认参数、关键字参数不定长参数一定要在关键字参数之前,不然会报错
函数并非总是直接显示输出,相反,它可以处理一些数据,并返回一个或一组值。
函数返回的值被称为返回值 。
在函数中,可使用return 语句将值返回到调用函数的代码行
如果不加return会默认返回一个None值
#下面来看一个函数,它接受名和姓并返回整洁的姓名:
def get_formatted_name(first_name, last_name):
"""返回整洁的姓名"""
full_name = first_name + ' ' + last_name
return full_name.title()
musician = get_formatted_name('jimi', 'hendrix')
print(musician)
#输出:
Jimi Hendrix
当函数体内没有retrun语句时,会默认返回一个None值
示例如下:
#不加return
def add(x,y):
print(x+y)
ret = add(1,2)
print(ret)
#输出:
3
None
#加上return:
def add(x,y):
return x+y
ret = add(1,2)
print(ret)
#输出:
3
如果想要返回多个值,只需要return后的值中间以逗号相隔即可
例如:
def cal(x,y):
return x+y,x*y
ret = cal(3,7) #一个变量接受,打印值就合在一个元组内
print(ret)
#分开打印,用两个变量接受:
he,cheng = cal(3,7)
print(he,cheng)
#输出:
(10, 21)
10 21
有时候,需要让实参变成可选的,这样使用函数的人就只需在必要时才提供额外的信息,可使用默认值来让实参变成可选的。
例如,假设我们要扩展函数get_formatted_name() ,使其还处理中间名。为此,可将其修改成类似于下面这样:
def get_formatted_name(first_name, middle_name, last_name):
full_name = first_name + ' ' + middle_name + ' ' + last_name
return full_name.title()
musician = get_formatted_name('john', 'lee', 'hooker')
print(musician)
#输出:
John Lee Hooker
然而,并非所有的人都有中间名,但如果你调用这个函数时只提供了名和姓,它将不能正确地运行。
为让中间名变成可选的,可给实参middle_name 指定一个默认值——空字符串,并在用户没有提供中间名时不使用这个实参。
为让get_formatted_name() 在没有提供中间名时依然可行,可给实参middle_name 指定一个默认值——空字符串,并将其移到形参列表的末尾:
def get_formatted_name(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()
musician = get_formatted_name('jimi', 'hendrix')
print(musician)
musician = get_formatted_name('john', 'hooker', 'lee')
print(musician)
#输出:
Jimi Hendrix
John Lee Hooker
当前,字符串'jimi' 和'hendrix' 被标记为名和姓。
还可以轻松地扩展这个函数,使其接受可选值,如中间名、年龄、职业或你要存储的其他任何信息。
例如,下面的修改让你还能存储年龄:
def build_person(first_name, last_name, age=''):
"""返回一个字典,其中包含有关一个人的信息"""
person = {'first': first_name, 'last': last_name}
if age:
person['age'] = age
return person
musician = build_person('jimi', 'hendrix', age=27)
print(musician)
print("*" * 100)
#输出:
{'first': 'jimi', 'last': 'hendrix', 'age': 27}
while 循环存在一个问题:没有定义退出条件。
请用户提供一系列输入时,该在什么地方提供退出条件呢?
我们要让用户能够尽可能容易地退出,因此每次提示用户输入时,都应提供退出途径。
每次提示用户输入时,都使用break 语句提供了退出循环的简单途径:
def get_formatted_name(first_name, last_name):
"""返回整洁的姓名"""
full_name = first_name + ' ' + last_name
return full_name.title()
while True:
print("\nPlease tell me your name:")
print("(enter 'q' at any time to quit)")
f_name = input("First name: ")
if f_name == 'q':
break
l_name = input("Last name: ")
if l_name == 'q':
break
formatted_name = get_formatted_name(f_name, l_name)
print("\nHello, " + formatted_name + "!")
#输出:
Please tell me your name:
(enter 'q' at any time to quit)
First name: 小红
Last name: 小明
Hello, 小红 小明!
Please tell me your name:
(enter 'q' at any time to quit)
First name: q
你经常会发现,向函数传递列表很有用,这种列表包含的可能是名字、数字或更复杂的对象(如字典)。
将列表传递给函数后,函数就能直接访问其内容。下面使用函数来提高 处理列表的效率。
假设有一个用户列表,我们要问候其中的每位用户。
如下示例:
将一个名字列表传递给一个名为greet_users() 的函数,这个函数问候列表中的每个人:
def greet_users(names):
"""向列表中的每位用户都发出简单的问候"""
for name in names:
msg = "Hello, " + name.title() + "!"
print(msg)
usernames = ['hannah', 'ty', 'margot']
greet_users(usernames)
#输出:
Hello, Hannah!
Hello, Ty!
Hello, Margot!
将列表传递给函数后,函数就可对其进行修改。
在函数中对这个列表所做的任何修改都是永久性的,这让你能够高效地处理大量的数据。
下面是一家为用户提交的设计制作3D打印模型的公司。需要打印的设计存储在一个列表中,打印后移到另一个列表中。下面是在不使用函数的情况下模拟这个过程的代码:
# 首先创建一个列表,其中包含一些要打印的设计
unprinted_designs = ['iphone case', 'robot pendant', 'dodecahedron']
completed_models = []
# 模拟打印每个设计,直到没有未打印的设计为止
# 打印每个设计后,都将其移到列表completed_models中
while unprinted_designs:
current_design = unprinted_designs.pop()
#模拟根据设计制作3D打印模型的过程
print("Printing model: " + current_design)
completed_models.append(current_design)
# 显示打印好的所有模型
print("\n The following models have been printed:")
for completed_model in completed_models:
print(completed_model)
#输出:
Printing model: dodecahedron
Printing model: robot pendant
Printing model: iphone case
The following models have been printed:
dodecahedron
robot pendant
iphone case
为重新组织这些代码,我们可编写两个函数,每个都做一件具体的工作。
大部分代码都与原来相同,只是效率更高。
第一个函数将负责处理打印设计的工作,而第二个将概述打印了哪些设计:
def print_models(unprinted_designs, completed_models):
""" 模拟打印每个设计,直到没有未打印的设计为止
打印每个设计后,都将其移到列表completed_models中
"""
while unprinted_designs:
current_design = unprinted_designs.pop()
# 模拟根据设计制作3D打印模型的过程
print("Printing model: " + current_design)
completed_models.append(current_design)
def show_completed_models(completed_models):
"""显示打印好的所有模型"""
print("\nThe following models have been printed:")
for completed_model in completed_models:
print(completed_model)
unprinted_designs = ['iphone case', 'robot pendant', 'dodecahedron']
completed_models = []
print_models(unprinted_designs, completed_models)
show_completed_models(completed_models)
#输出:
Printing model: iphone case
The following models have been printed:
iphone case
递归是指在函数中调用自身的过程。
递归函数通常包括两部分:基线条件和递归条件。
基线条件是指递归过程中最简单的情况,当满足基线条件时,递归就不再继续,避免无限循环。
递归条件则是指递归过程中需要调用自身的情况。
如下示例:求阶乘:5的阶乘 5*4*3*2*1
循环和递归的两种写法对比:
# 1、for循环的写法:
n = int(input("请输入整数:"))
a = 1
for i in range(1,n+1):
a *= i
print(a)
print("*" * 100)
#输出:
请输入整数:8
40320
# 2、递归的写法:
n = int(input("请输入整数:"))
def f1(n): #此函数返回n*f1(n-1) ;
if n == 1: #递归退出的条件
return 1
return n*f1(n-1)
print(f1(n))
#输出:
请输入整数:8
40320
使用斐波那契数列的方式,传入任意一个数字,求出对应的值
斐波那契规律:从第三个数开始,每个数等于前面两个数的和,例如:0,1,1,2,3,5,8,13……
def f1(n): #求的是当前第n个数的值 f1(5) = f1(4)+f1(3)
if n == 1:
return 0
elif n == 2:
return 1
return f1(n-1) + f1(n-2)
a = f1(8)
print(a)
#输出:
13
时间戳是指:格林威治时间 1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数。
import time #导入python内置时间包
timeStamp = time.time() #获取当前时间戳,到秒
print(timeStamp)
time.time()*1000 #求毫秒级的时间戳
#输出:
1697845379.406788
多用于web自动化,等待元素出现
import time
time.sleep(3) #等待几秒,再执行后续的代码;
print(3333)
#输出:
3333
#获取当前日期+时间:
import datetime #导入python内置-datetime日期文件包
a = datetime.datetime.now() #获取当前日期+时间
print(a)
from datetime import datetime #导入datetime这个文件的datetime类
a = datetime.now() #导入datetime类之后,就不需要再写datetime文件名
print(a)
#输出:
2023-10-21 07:45:33.232610
2023-10-21 07:45:33.232610
#获取年份:
year =datetime.now().year
print(f"今年是{year}年")
#获取月份:
month = datetime.now().month
print(f"现在是{month}月")
# 获取日期:
day = datetime.now().day
print(f"今天是{day}号")
# 获取今天星期几:星期一是从0开始的,星期一就是返回0
weekday = datetime.now().weekday()
print(f"今天是星期{weekday + 1}") # weekday+1,进行转换
#输出:
今年是2023年
现在是10月
今天是21号
今天是星期6
针对有些时候datetime返回的时间格式、并不是我们想要的
我们可能只需要年份货到月份、或日期:
strftime:时间格式化,方法引用,大小写格式固定
也可以:strftime("%Y/%m/%d %H:%M:%S")
%Y:年份
%m:月份
%d:日
%H:小时
%M:分钟
%S:秒
a = datetime.now().strftime("%Y-%m-%d %H:%M:%S") #把时间类型转换成字符串
print(a)
#输出:
2023-10-21 07:49:49
from datetime import datetime
from datetime import timedelta
#获取当周周一的日期:
weekday = datetime.now().weekday()
# print(type(weekday)) # 类型为init
diff = timedelta(weekday) #转换为日期格式
print(type(diff))
print(diff)
b = datetime.now() - diff
print(b)
#输出:
2023-10-21 07:51:23
5 days, 0:00:00
2023-10-16 07:51:23.409633
a = "2021-05-04 12:00:00"
new_a = datetime.strptime(a,"%Y-%m-%d %H:%M:%S")
print(new_a)
diff = datetime.now() - new_a
print(f"时间差为{diff}")
#输出:
2023-10-21 07:52:45
2021-05-04 12:00:00
时间差为899 days, 19:52:45.055671
import calendar
import datetime
a = calendar.monthrange(2022,2)
print(a)
b = datetime.date(2022,2,a[1]) #将年月日拼接起来,获取指定年的月份的最后一天
print(b)
#输出:
(1, 28)
2022-02-28