Python基础语法文章导航:
- Python基础(01初识数据类型&变量)
- Python基础(02条件&循环语句)
- Python基础(03字符串格式化&运算符&进制&编码)
- Python基础(04 基础练习题)
- Python数据类型(day05整型&布尔类型&字符串类型)
- Python数据类型(06列表&元组)
- Python数据类型(07集合&字典&浮点型&None)
- Python文件操作01(自动化测试文件相关操作)
目录
一.函数的定义
函数,可以当做是一大堆功能代码的集合。
def 函数名():
函数内编写代码
...
...
函数名()
什么时候用到函数:
有重复代码,用函数增加代码的重用性。
def send_email():
# 10行代码
print("欢迎使用计算机监控系统")
if CPU占用率 > 90%:
send_email()
if 硬盘使用率 > 99%:
send_email()
if 内存使用率 > 98%:
send_email()
...
代码太长,用函数增强代码的可读性。
def calculate_same_num_rule():
"""判断是否是豹子"""
pass
def calculate_same_color_rule():
"""判断是否是同花"""
pass
def calculate_straight_rule():
"""判断是否顺子"""
pass
def calculate_double_card_rule(poke_list):
"""判断是否对子"""
pass
def calculate_single_card_rule():
"""判断是否单牌"""
pass
# 1. 生成一副扑克牌
card_color_list = ["红桃", "黑桃", "方片", "梅花"]
card_nums = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] # A
all_card_list = [[color, num] for color in card_color_list for num in card_nums]
# 2.洗牌
random.shuffle(all_card_list)
# 3.给玩家发牌
...
# 4.判断牌是:豹子?同花顺?顺子?对子?单点?
calculate_same_num_rule()
calculate_same_color_rule()
calculate_straight_rule()
...
二.函数的参数
使用python发邮件,然后再由此引出函数的参数。
-
注册邮箱
-
基础配置
-
授权码
-
SMTP服务器: smtp.126.com
-
-
代码发送邮件
基于函数的参数(将代码中动态部分提取到参数位置,让函数可以充分被重用)。以后邮件发送的代码可参考这块,理解即可。
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr
def send_email(email):
# ### 1.邮件内容配置 ###
# 邮件文本
msg = MIMEText("认真工作了吗", 'html', 'utf-8')
# 邮件上显示的发件人
msg['From'] = formataddr(["匿名上班人", "wptawy@126.com"])
# 邮件上显示的主题
msg['Subject'] = "邮件主题"
# ### 2.发送邮件 ###
server = smtplib.SMTP_SSL("smtp.126.com")
server.login("wptawy@126.com", "WIYSAILOVUKPQGHY")
server.sendmail("wptawy@126.com", email, msg.as_string())
server.quit()
v1 = "424662508@qq.com"
send_email(v1)
v2 = "424662509@qq.com"
send_email(v2)
v3 = "wupeiqi@live.com"
send_email(v3)
1.参数
形式参数
在定义函数时,如果在括号中添加变量
,我们称它为函数的形式参数:
# ###### 定义有三个参数的函数(a1/a2/a3一般称为形式参数-形参) #####
def func(a1,a2,a3):
print(a1+a2+a3)
# 执行函数并传入参数(执行函数传值时一般称为实际参数-实参)
func(11,22,33)
位置传参
def add(n1,n2):
print(n1+n2)
add(1,22)
关键字传参
def add(n1,n2):
print(n1+n2)
add(n1=1,n2=22)
2. 默认参数(定义时使用)
def func(a1, a2, a3=10):
print(a1 + a2 + a3)
# 位置传参
func(8, 19)
func(1, 2, 99)
# 关键字传参(位置和关键混合时,关键字传参要在后面)
func(12, 9, a3=90)
func(12, a2=9, a3=90)
func(a1=12, a2=9, a3=90)
3.动态参数(定义时使用)
(1)*
def func(*args):
print(args) # 元组类型 (22,) (22, 33) (22,33,99,) ()
# 只能按照位置传参
func(22)
func(22,33)
func(22,33,99)
func()
(2)**
def func(**kwargs):
print(kwargs) # 字典类型 {"n1":"武沛齐"} {'n1': '武沛齐', 'age': 18} {"n1":"武沛齐","age":"18","email":"xxxx"} {}
# 只能按关键字传参
func(n1="武沛齐")
func(n1="武沛齐", age=18)
func(n1="武沛齐", age=18, email="xx@live.com")
func()
(3)*,**
执行函数,并传入参数的时候:元组形式是*的实参,字典形式是**的实参
def func(*args,**kwargs):
print(args,kwargs)
func(22,33,99)
func(n1="武沛齐",age=18)
func(22,33,99,n1="武沛齐",age=18)
func()
#(22, 33, 99) {}
# () {'n1': '武沛齐', 'age': 18}
# (22, 33, 99) {'n1': '武沛齐', 'age': 18}
# () {}
字符串格式化时的format功能,就用到位置传参和关键字传参的形式
v1="我叫{},今年{},性别{}".format("晓鹏",26,"女")
print(v1)
v2="我叫{name},今年{age},性别{sex}".format(name="晓鹏",age=26,sex="女")
print(v2)
注意事项
# 1. ** 必须放在 * 的后面
def func1(*args, **kwargs):
print(args, **kwargs)
# 2. 参数和动态参数混合时,动态参数只能放在最后。
def func2(a1, a2, a3, *args, **kwargs):
print(a1, a2, a3, args, **kwargs)
# 3. 默认值参数和动态参数同时存在
def func3(a1, a2, a3, a4=10, *args, a5=20, **kwargs):
print(a1, a2, a3, a4, a5, args, kwargs) #11 22 33 44 20 (55, 66, 77) {'a10': 123}
func3(11, 22, 33, 44, 55, 66, 77, a5=10, a10=123) #a4有位置传参,覆盖原来的默认值参数
4.函数返回值
(1)返回值可以是任意类型,如果函数中没写return,则默认返回None
def func():
value = 1 + 1
ret = func()
print(ret) # None
def func():
return [1,True,(11,22,33)]
result = func()
print(result)
当在函数中写 return
或 return None
,执行函数获取的返回值都是None。
def func():
value = 1 + 1
# return # 或
return None
ret = func()
print(ret) # None
(2)return后面的值如果有逗号,则默认会将返回值转换成元组再返回。
def func():
return 1,2,3
value = func()
print(value) # (1,2,3)
(3)函数一旦遇到return就会立即退出函数(终止函数中的所有代码)
def func():
print(1)
return "结束吧"
print(2)
ret = func()
print(ret)
#1
#结束吧
def func():
print(1)
for i in range(10):
print(i)
for j in range(100):
print(j)
return
print(2)
result = func()
print(result)
# 输出
1
0
0
None
三. 参数内存地址相关
如果想要查看下某个值的在内存中的地址?
v1 = "武沛齐"
addr = id(v1)
print(addr) # 140691049514160
记住一句话:函数执行传参时,传递的是内存地址。
def func(data):
print(data, id(data)) # 武沛齐 140247057684592
v1 = "武沛齐"
print(id(v1)) # 140247057684592
func(v1)
四. 函数的返回值的内存地址
def func():
data = [11, 22, 33]
return data
v1 = func()
print(v1) # [11,22,33]
上述代码的执行过程:
执行func函数
data = [11, 22, 33]
创建一块内存区域,内部存储[11,22,33]
,data变量指向这块内存地址。
return data
返回data指向的内存地址v1接收返回值,所以 v1 和 data 都指向
[11,22,33]
的内存地址(两个变量指向此内存,引用计数器为2)由函数执行完毕之后,函数内部的变量都会被释放。(即:删除data变量,内存地址的引用计数器-1)
所以,最终v1指向的函数内部创建的那块内存地址。
五.参数的默认值(面试题)
原理:Python在创建函数(未执行)时,如果发现函数的参数中有默认值,则在函数内部会创建一块区域并维护这个默认值。
def func(a1,a2=18):
print(a1,a2)
#执行函数未传值时,则让a2指向 函数维护的那个值的地址。
func("root") #root 18
#执行函数传值时,则让a2指向新传入的值的地址。
func("admin",20) #admin 20
在特定情况【默认参数的值是可变类型 list/dict/set】 & 【函数内部会修改这个值】下,参数的默认值 有坑 。坑的点:如果发现函数的参数中有默认值,则在函数内部会创建一块区域并维护这个默认值。如果执行函数的实参(按照位置传参,对应函数的参数中也有默认值),则优先按照实参新创建一块区域 维护新的默认值,然后去进行执行函数。
# 在函数内存中会维护一块区域存储 [1,2,666,666,666] 100010001
def func(a1,a2=[1,2]):
a2.append(666)
print(a1,a2)
# a1=100
# a2 -> 100010001
func(100) # 100 [1,2,666]
# a1=200
# a2 -> 100010001
func(200) # 200 [1,2,666,666]
# a1=99
# a2 -> 1111111101
func(99,[77,88]) # 99 [77,88,666]
# a1=300
# a2 -> 100010001
func(300) # 300 [1,2,666,666,666]
大坑:就是操作之后立马进行输出,输出的话输出的是内存地址对应的值。
# 在内部会维护一块区域存储 [1, 2, 10, 20,40 ] ,内存地址 1010101010
def func(a1, a2=[1, 2]):
a2.append(a1)
return a2
# a1=10
# a2 -> 1010101010
# v1 -> 1010101010
v1 = func(10)
print(v1) # [1, 2, 10]
# a1=20
# a2 -> 1010101010
# v2 -> 1010101010
v2 = func(20)
print(v2) # [1, 2, 10, 20 ]
# a1=30
# a2 -> 11111111111 [11, 22,30]
# v3 -> 11111111111
v3 = func(30, [11, 22])
print(v3) # [11, 22,30]
# a1=40
# a2 -> 1010101010
# v4 -> 1010101010
v4 = func(40)
print(v4) # [1, 2, 10, 20,40 ]
深坑:完成所有操作之后进行输出,输出的话输出的是内存地址对应的值。
# 内存中创建空间存储 [1, 2, 10, 20, 40] 地址:1010101010
def func(a1, a2=[1, 2]):
a2.append(a1)
return a2
# a1=10
# a2 -> 1010101010
# v1 -> 1010101010
v1 = func(10)
# a1=20
# a2 -> 1010101010
# v2 -> 1010101010
v2 = func(20)
# a1=30
# a2 -> 11111111111 [11,22,30]
# v3 -> 11111111111
v3 = func(30, [11, 22])
# a1=40
# a2 -> 1010101010
# v4 -> 1010101010
v4 = func(40)
print(v1) # [1, 2, 10, 20, 40]
print(v2) # [1, 2, 10, 20, 40]
print(v3) # [11,22,30]
print(v4) # [1, 2, 10, 20, 40]
六. 动态参数
在定义函数时可以用*和
**,其实在执行函数时,也可以用。
(1)形参固定,实参用*和**
def func(a1,a2):
print(a1,a2)
func( 11, 22 )
func( a1=1, a2=2 )
func( *[11,22] )
func( **{"a1":11,"a2":22} )
#11 22
# 1 2
# 11 22
# 11 22
(2)形参用*和
**,实参也用*和
**
def func(*args,**kwargs):
print(args,kwargs)
func( 11, 22 )
func( 11, 22, name="武沛齐", age=18 )
# 小坑,([11,22,33], {"k1":1,"k2":2}), {}
func( [11,22,33], {"k1":1,"k2":2} )
# args=(11,22,33),kwargs={"k1":1,"k2":2}
func( *[11,22,33], **{"k1":1,"k2":2} )
# 值得注意:按照这个方式将数据传递给args和kwargs时,数据是会重新拷贝一份的(可理解为内部循环每个元素并设置到args和kwargs中)。
# (11, 22) {}
# (11, 22) {'name': '武沛齐', 'age': 18}
# ([11, 22, 33], {'k1': 1, 'k2': 2}) {}
# (11, 22, 33) {'k1': 1, 'k2': 2}
(3)使用format字符串格式化时,可以这样:
v1 = "我是{},年龄:{}。".format("武沛齐",18)
v2 = "我是{name},年龄:{age}。".format(name="武沛齐",age=18)
v3 = "我是{},年龄:{}。".format(*["武沛齐",18])
v4 = "我是{name},年龄:{age}。".format(**{"name":"武沛齐","age":18})
七.函数和函数名
函数名其实就是一个变量,这个变量只不过代指的函数而已。
注意:函数必须先定义才能被调用执行(解释型语言)。
# 正确
def add(n1,n2):
return n1 + n2
ret = add(1,2)
print(ret)
1. 函数做元素
既然函数就相当于是一个变量,那么在列表等元素中可以把函数当做元素。注意:函数同时也可被哈希,所以函数名通知也可以当做 集合的元素、字典的键。
def func():
return 123
data_list = ["武沛齐", "func", func , func() ]
print( data_list[0] ) # 字符串"武沛齐"
print( data_list[1] ) # 字符串 "func"
print( data_list[2] ) # 函数 func
print( data_list[3] ) # 整数 123
res = data_list[2]()
print( res ) # 执行函数 func,并获取返回值;print再输出返回值。
print( data_list[2]() ) # 123
情景1,例如:要开发一个类似于微信的功能:
def send_message():
"""发送消息"""
pass
def send_image():
"""发送图片"""
pass
print("欢迎使用xx系统")
print("请选择:1.发送消息;2.发送图片")
choice = input("输入选择的序号")
if choice == "1":
send_message()
elif choice == "2":
send_image()
else:
print("输入错误")
改成字典的形式:
def send_message():
"""发送消息"""
pass
def send_image():
"""发送图片"""
pass
function_dict = {
"1": send_message,
"2": send_image
}
print("欢迎使用xx系统")
print("请选择:1.发送消息;2.发送图片")
choice = input("输入选择的序号") # "1"
func = function_dict.get(choice)
if not func:
print("输入错误")
else:
# 执行函数
func()
情景2,例如:某个特定情况,要实现发送短信、微信、邮件。
def send_msg():
"""发送短信"""
pass
def send_email():
"""发送图片"""
pass
def send_wechat():
"""发送微信"""
# 执行函数
send_msg()
send_email()
send_wechat()
def send_msg():
"""发送短信"""
pass
def send_email():
"""发送图片"""
pass
def send_wechat():
"""发送微信"""
pass
func_list = [ send_msg, send_email, send_wechat ]
for item in func_list:
item()
上述两种情景,在参数相同时才可用,如果参数不一致,会出错。所以,在项目设计时就要让程序满足这一点,如果无法满足,也可以通过其他手段时间,例如:
情景3:
def send_message(phone,content):
"""发送消息"""
pass
def send_image(img_path, content):
"""发送图片"""
pass
def send_emoji(emoji):
"""发送表情"""
pass
def send_file(path):
"""发送文件"""
pass
function_dict = {
"1": [ send_message, ['15131255089', '你好呀']],
"2": [ send_image, ['xxx/xxx/xx.png', '消息内容']],
"3": [ send_emoji, ["😁"]],
"4": [ send_file, ['xx.zip'] ]
}
print("欢迎使用xx系统")
print("请选择:1.发送消息;2.发送图片;3.发送表情;4.发送文件")
choice = input("输入选择的序号") # 1
item = function_dict.get(choice) # [ send_message, ['15131255089', '你好呀']],
if not item:
print("输入错误")
else:
# 执行函数
func = item[0] # send_message
param_list = item[1] # ['15131255089', '你好呀']
func(*param_list) # send_message(*['15131255089', '你好呀'])
情景4:
def send_msg(mobile, content):
"""发送短信"""
pass
def send_email(to_email, subject, content):
"""发送图片"""
pass
def send_wechat(user_id, content):
"""发送微信"""
pass
func_list = [
{"name": send_msg, "params": {'mobile': "15131255089", "content": "你有新短消息"}},
{"name": send_email, "params": {'to_email': "wupeiqi@live.com", "subject": "报警消息", "content": "硬盘容量不够用了"}},
{"name": send_wechat, "params": {'user_id': 1, 'content': "约吗"}},
]
# {"name": send_msg, "params": {'mobile': "15131255089", "content": "你有新短消息"}},
for item in func_list:
func = item['name'] # send_msg
param_dict = item['params'] # {'mobile': "15131255089", "content": "你有新短消息"}
func(**param_dict) # send_msg(**{'mobile': "15131255089", "content": "你有新短消息"})
2. 函数名赋值
将函数名赋值给其他变量,函数名其实就个变量,代指某函数;如果将函数名赋值给另外一个变量,则此变量也会代指该函数,例如:
def func(a1,a2):
print(a1,a2)
xxxxx = func
# 此时,xxxxx和func都代指上面的那个函数,所以都可以被执行。
func(1,1)
xxxxx(2,2)
对函数名重新赋值,如果将函数名修改为其他值,函数名便不再代指函数,例如
def func(a1,a2):
print(a1,a2)
# 执行func函数
func(11,22)
# func重新赋值成一个字符串
func = "武沛齐"
print(func)
注意:由于函数名被重新定义之后,就会变量新被定义的值,所以大家在自定义函数时,不要与python内置的函数同名,否则会覆盖内置函数的功能,例如:
# len内置函数用于计算值得长度
v1 = len("武沛齐")
print(v1) # 3
# len重新定义成另外一个函数
def len(a1,a2):
return a1 + a2
# 以后执行len函数,只能按照重新定义的来使用
v3 = len(1,2)
print(v3)
3. 函数名做参数和返回值
函数名其实就一个变量,代指某个函数,所以,他和其他的数据类型一样,也可以当做函数的参数和返回值。
参数
def plus(num):
return num + 100
def handler(func):
res = func(10) # 110
msg = "执行func,并获取到的结果为:{}".format(res)
print(msg) # 执行func,并获取到的结果为:110
# 执行handler函数,将plus作为参数传递给handler的形式参数func
handler(plus)
返回值
def plus(num):
return num + 100
def handler():
print("执行handler函数")
return plus
result = handler()
data = result(20) # 120
print(data)
八.返回值和print
这两个函数是完全不同的
-
在函数中使用print,只是用于在某个位置输出内容而已。
-
在函数中使用return,是为了将函数的执行结果返回给调用者,以便于后续其他操作。
def f1():
print(123)
def f2(arg):
ret = arg()
return f1
v1 = f2(f1)
v2 = v1()
print(v2)
# 输出
123
123
None
九.作用域
作用域,可以理解为一块空间,这块空间的数据是可以共享的。通俗点来说,作用域就类似于一个房子,房子中的东西归里面的所有人共享,其他房子的人无法获取。
1.函数作为作用域
Python以函数为作用域,所以在函数内创建的所有数据,可以此函数中被使用,无法在其他函数中被使用。
def func():
name = "武沛齐"
data_list = [11,22,33,44]
print(name,data_list)
age = 20
print(age)
def handler():
age = 18
print(age)
func()
handler()
2.全局和局部
Python中以函数为作用域,函数的作用域其实是一个局部作用域。
# 全局变量(变量名大写)
COUNTRY = "中国"
CITY_LIST = ["北京","上海","深圳"]
def download():
# 局部变量
url = "http://www.xxx.com"
...
def upload():
file_name = "rose.zip"
...
COUNTRY
和CITY_LIST
是在全局作用域中,全局作用域中创建的变量称之为【全局变量】,可以在全局作用域中被使用,也可以在其局部作用域中被使用。
download
和upload
函数内部维护的就是一个局部作用域,在各自函数内部创建变量称之为【局部变量】,且局部变量只能在此作用域中被使用。局部作用域中想使用某个变量时,寻找的顺序为:优先在局部作用域中寻找,如果没有则去上级作用域中寻找
。
注意:全局变量一般都是大写。
示例1:在局部作用域中读取全局作用域的变量。
COUNTRY = "中国"
CITY_LIST = ["北京","上海","深圳"]
def download():
url = "http://www.xxx.com"
print(url)
print(COUNTRY)
print(CITY_LIST)
def upload():
file_name = "rose.zip"
print(file_name)
print(COUNTRY)
print(CITY_LIST)
print(COUNTRY) #全局变量 对
print(CITY_LIST) #全局变量 对
download() #全局变量 对
upload() #全局变量 对
print(file_name) # 报错 file_name是upload()的局部变量
print(url) # 报错 url是download()的局部变量
示例2:局部作用域和全局作用域变量同名,优先读局部变量作用域里面变量的值。
COUNTRY = "中国"
CITY_LIST = ["北京","上海","深圳"]
def download():
url = "http://www.xxx.com"
CITY_LIST = ["河北","河南","山西"]
print(url)
print(COUNTRY)
print(CITY_LIST)
def upload():
file_name = "rose.zip"
print(COUNTRY)
print(CITY_LIST)
print(COUNTRY)
print(CITY_LIST)
download()
upload()
COUNTRY = "中华人民共和共国"
CITY_LIST = [11,22,33]
download()
upload()
# 输出
中国
["北京","上海","深圳"]
http://www.xxx.com
中国
["河北","河南","山西"]
中国
["北京","上海","深圳"]
http://www.xxx.com
中华人民共和共国
["河北","河南","山西"]
中华人民共和共国
[11,22,33]
3.global关键字
默认情况下,在局部作用域对全局变量只能进行:读取和修改内部元素(可变类型),无法对全局变量进行重新赋值。
读取
COUNTRY = "中国"
CITY_LIST = ["北京","上海","深圳"]
def download():
url = "http://www.xxx.com"
print(COUNTRY)
print(CITY_LIST)
download()
修改内部元素(可变类型)
COUNTRY = "中国"
CITY_LIST = ["北京","上海","深圳"]
def download():
url = "http://www.xxx.com"
print(CITY_LIST)
CITY_LIST.append("广州")
CITY_LIST[0] = "南京"
print(CITY_LIST)
download()
# ['北京', '上海', '深圳']
# ['南京', '上海', '深圳', '广州']
无法对全局变量重新赋值
COUNTRY = "中国"
CITY_LIST = ["北京","上海","深圳"]
def download():
url = "http://www.xxx.com"
# 不是对全部变量赋值,而是在局部作用域中又创建了一个局部变量 CITY_LIST 。
CITY_LIST = ["河北","河南","山西"]
print(CITY_LIST)
def upload():
file_name = "rose.zip"
print(COUNTRY)
print(CITY_LIST)
download()
upload()
如果想要在局部作用域中对全局变量重新赋值,则可以基于 global
关键字实现,例如:
COUNTRY = "中国"
CITY_LIST = ["北京","上海","深圳"]
def download():
url = "http://www.xxx.com"
global CITY_LIST
CITY_LIST = ["河北","河南","山西"]
print(CITY_LIST)
global COUNTRY
COUNTRY = "中华人民共和国"
print(COUNTRY)
def upload():
file_name = "rose.zip"
print(COUNTRY)
print(CITY_LIST)
download()
upload()
#['河北', '河南', '山西']
#中华人民共和国
#中华人民共和国
#['河北', '河南', '山西']
总结:
以上为函数的一些基础知识,需进行熟练掌握,方便后续更深一层次对函数的学习。