目录
python函数详解
函数
引言:
比如植物大战僵尸,这个游戏本身也是由代码编写,现在假设有一种豌豆射手,每发射一次 炮弹会执行100行逻辑代码
如果我在程序,每当需要发射炮弹的时候,都要编写100行逻辑代码,就会觉得该程序过于冗余, 代码重复度较高。
解决方案:
如果我将这100行代码放到一个区域中,然后给这个区域起一个名字,今后在需要发射炮弹的代码 逻辑中,通过这个名字就可以调用起这100行代码。这个区域【代码段】在python中称之为函 数,先将函数定义出来,并对该函数起一个名字,将来在合适的地方通过函数名调用该函数,执行 该函数的内部逻辑。
函数的定义
-
语句定义格式:
# 使用python中的关键字 def def 函数名(...): 函数代码逻辑
函数的使用特点
- 函数不调用不执行
- 定义函数必须在调用之前出现
函数的参数
-
参数种类
-
形式参数:指的是函数定义时,小括号中定义的参数
-
实际参数:指的是将来调用函数时,实际传入进去的具体的值
def fun1(hhs, zcy): # hhs zcy是形式参数,名字自定义 print(hhs + zcy) a1 = int(input("请输入第一个数值:")) b1 = int(input("请输入第二个数值:")) fun1(a1,b1) # a1 b1 是实际参数,可以是变量,也可以是具体的值本身
-
-
参数的传值方式
- 位置传参
def show1(a, b, c): print(f"a:{a},b:{b},c:{c}") # a:11,b:22,c:33 show1(11, 22, 33)
-
关键字传参【通过传参的名字传参】
def show1(a, b, c): print(f"a:{a},b:{b},c:{c}") show1(b=100, c=200, a=300)
-
混合传参
def show1(a, b, c): print(f"a:{a},b:{b},c:{c}") show1(100, c=200, b=300)
注意:混合传参的时候,前面没有关键字的实参是会按照形参的位置来的,后面关键字传参可以顺序不一样。
函数传参的场景【扩展知识】
# 未使用函数 ''' 编写登录时的逻辑代码 ''' 编写发送邮件的代码 50行 ''' 编写注册时的逻辑代码 ''' 编写发送邮件的代码 50行
# 使用函数 def send_email(xxx,xxx): 编写发送邮件的代码 ''' 编写登录时的逻辑代码 ''' send_email(xxx,xxx) ''' 编写注册时的逻辑代码 ''' send_email(xxx,xxx)
def send_email(msg_to, send_info): import smtplib from email.mime.text import MIMEText from email.header import Header msg_from = 'XXXXXXX@qq.com' # 发送方邮箱 passwd = 'XXXXXXXXXXXXXXX' # 填入发送方邮箱的授权码(填入自己的授权码,相当于邮 箱密码) # msg_to = ['','',''] subject = "33期邮件信息" # 主题 # 生成一个MIMEText对象(还有一些其它参数) msg = MIMEText(send_info) # 放入邮件主题 msg['Subject'] = subject # 也可以这样传参 # msg['Subject'] = Header(subject, 'utf-8') # 放入发件人 msg['From'] = msg_from # 放入收件人 # 通过ssl方式发送,服务器地址,端口 s = smtplib.SMTP_SSL("smtp.qq.com", 465) # 登录到邮箱 s.login(msg_from, passwd) # 发送邮件:发送方,收件方,要发送的消息 s.sendmail(msg_from, msg_to, msg.as_string()) print('成功') p = input("请输入要接收邮件的qq邮箱地址:") info = input("请输入要发送的内容:") send_email(p, info)
-
默认值传参
需求:调用一个函数,传入一个大字符串和一个小字符串,查找小字符串在大字符串中出现 的次数,调用函数的时候,可以不传小字串,默认查找字符’a’在大字符串中的出现次数。
def str_number(big_str, small_str='a'): # 定义函数时,可以设置形式参数的值,作为默 认值 # dadqwwfwqfjwqaoiaiosijpoaospjfqwasaapjosaja list1 = list(big_str) counts = list1.count(small_str) print(f"{small_str}在大字符串中总共出现了{counts}次。。。") str_number('dadqwwfwqfjwqaoiaiosijpoaospjfqwasaapjosaja') # 调用时若不传入第二个 参数,使用的就是定义时的默认值 str_number('dadqwwfwqfjwqaoiaiosijpoaospjfqwasaapjosaja','f') # 若传入第二个参 数,使用的就是实际传入的值
-
动态传参
未使用动态参数时,解决需求,比较麻烦,参数需要另外定义一个函数
# 需求1:定义一个函数,将来传入两个int类型的值求和 def sum1(a, b): print(a + b) sum1(10, 20) def sum2(a, b, c): print(a + b + c) # 需求2:定义一个函数,将来传入三个int类型的值求和 sum2(10, 20, 30)
使用动态参数,只需要定义一个函数就可以了
def sum1(*num): # 这里的num 是一个元组,接收若干个将来调用时传入的实参 n = 0 for i in num: n = n + i print(f"总和为:{n}") sum1(10, 20) # (10, 20) sum1(10, 20, 30) # (10, 20, 30) sum1(10, 20, 30, 40) # (10, 20, 30, 40)
使用动态参数时的注意事项
-
传参的内容,多个参数的类型可以是不一样的
def sum1(*num): # 这里的num 是一个元组,接收若干个将来调用时传入的实参 # n = 0 # for i in num: # n = n + i # print(f"总和为:{n}") print(num, type(num)) # sum1(10, 20) # (10, 20) # sum1(10, 20, 30) # (10, 20, 30) # sum1(10, 20, 30, 40) # (10, 20, 30, 40) # sum1(11) # (11,) # sum1(11, '小虎', [11, 22, 33]) # (11, '小虎', [11, 22, 33]) sum1((11,22,33)) # ((11, 22, 33),)
传入两个**的动态参数
def sum1(**num): print(num, type(num)) sum1(name='小虎', age=18)
结论:
* : 表示传入的每一个单独的元素都被封装成一个元组 ** : 表示传入的是一个一个的键值对,所有的键值对会被封装成一个字典 我们今后开发的时候,定义动态参数时,起名字是固定的,若一个*的动态参数,名字起为 *args, 若**的动态参数,名字起为**kwargs 动态参数定义的时候,必须
def show1(a, b, *args, **kwargs): print(args, type(args)) print(kwargs, type(kwargs)) # show1(11,22,33,44,name='小虎',address='合肥') show1(11, 22, 33, 44, name='小虎', address='合肥')
函数的返回值
有些函数,我们调用完之后,是能够得到结果的,理论上来说,python中所有的函数都有返回值
python中提供了一个关键字给我们在函数中使用,表示调用完后返回的值,这个关键字叫做return
-
例子
def sum1(a, b): c = a + b return c res1 = sum1(10, 20) print(res1) print(res1+20)
-
函数返回值的特点
-
一个函数中如果没有写return,默认情况下,这个函数最后一句话会有一个return None
-
return和print的区别?
- return是调用完函数时,可以返回一个值给调用者
- print就直接输出了,没有返回值
-
一个函数中,如果遇到return,那么这个函数就结束了,函数后续代码不执行
def fun1(a, b): print("今天是星期二") c = a + b return c print("明天自习") # 不执行 res1 = fun1(10, 20) print(res1)
-
一个函数只能有一个return
def fun1(a, b): print("今天是星期二") c = a + b return c print("明天自习") # 不执行 return 100 # 无效代码 res1 = fun1(10, 20) print(res1) def fun1(): for i in range(1,11): return i res1 = fun1() print(res1) # 1
-
函数返回值return后面,要返回的类型可以是任意的类型
-
-
函数参数和返回值的练习
-
定义 两个函数,第一个函数用户循环输入要累加的数值,函数内部将用户输入的值封装成一 个列表返回;第二个函数将第一个函数返回的列表当作参数传入,返回列表中所有数据之和的 结果。
def get_list(): list1 = [] while True: n = int(input("请输入一个数值:")) if n == -1: break list1.append(n) return list1 def sum_list(l2): n = 0 for i in l2: n += i return n l1 = get_list() print(l1) res2 = sum_list(l1) print(res2)
-
-
函数返回值的一些进阶用法
-
直接返回多个值,多个值之间用英文逗号分隔,实际返回的内容是一个元组
def show1(): return 11, 22, 33 res1 = show1() print(res1, type(res1))
-
分别接收每个返回元素的值
def show1(): return 11, 22, 33 a1, a2, a3 = show1() print(f"a1:{a1}") print(f"a2:{a2}") print(f"a3:{a3}")
def show1(): return 11, ['hello','world','python'], 33 a1, a2, a3 = show1() print(f"a1:{a1}") # a1:11 print(f"a2:{a2}") # a2:['hello', 'world', 'python'] print(f"a3:{a3}") # a3:33
-
函数的分类
-
无参无返回值
def login(): # 登录的操作逻辑 login()
-
无参有返回值
def get_number(): # 随机生成一个数 return num n = get_number() print(n)
-
有参无返回值
def sum1(a,b): print(a+b) sum1(10,20)
-
有参有返回值
def fun1(s1): return "shujia:" + s1 res1 = fun1('hello')
函数可以进行嵌套
-
嵌套调用
def fun1(): print("hello world 1") def fun2(): return 100 def fun3(a1, b1): fun1() # 调用fun1函数 res1 = fun2() # 调用fun2函数 return a1 + b1 + res1 res2 = fun3(11,22) print(res2)
-
嵌套定义
def fun1(): a = 10 def fun2(): print("hello world") print(a) fun2() # 不调用 不执行 fun1() fun2() # 报错,调用不了函数内部定义的函数
-
函数练习2:定义一个函数,传入一个文本路径,和一个关键词,将文本中包含关键词的那一句话 拿出来放在一个列表中返回该列表
import os def get_word_list(file_os, word): res_list = [] if os.path.exists(file_os): f = open(file_os, 'r', encoding='UTF-8') line_list = f.readlines() for line in line_list: if word in line: res_list.append(line.strip()) else: print("该路径不存在!!") return res_list list1 = get_word_list('data/words.txt', 'shujia') print(list1)
def get_word_list(file_os, word): res_list = [] if not os.path.exists(file_os): print("该路径不存在!!") return res_list f = open(file_os, 'r', encoding='UTF-8') line_list = f.readlines() for line in line_list: if word in line: res_list.append(line.strip()) return res_list list1 = get_word_list('data/words.txt', 'shujia') print(list1)
函数的传值问题
在python中,调用函数时,传入的是对象的引用。
不可变对象【str, int, float, bool】
def fun1(x, y): print(f"x:{x}, y:{y}") # x:hello, y:world x = y x = x + x print(f"x:{x}, y:{y}") # x:worldworld, y:world s1 = "hello" s2 = "world" print(f"s1:{s1}, s2:{s2}") # s1:hello, s2:world fun1(s1,s2) print(f"s1:{s1}, s2:{s2}") # s1:hello, s2:world
不可变对象:指的是函数内的操作不会影响到函数外的变量值
可变对象【list, tuple, dict, set, 类】
def fun1(x, y): print(f"x:{x}, y:{y}") # x:['hello'], y:['world'] x = y x.append('java') print(f"x:{x}, y:{y}") # x:['world','java'], y:['world','java'] s1 = ['hello'] s2 = ['world'] print(f"s1:{s1}, s2:{s2}") # s1:['hello'] , s2:['world'] fun1(s1,s2) print(f"s1:{s1}, s2:{s2}") # s1:['hello'], s2:['world', 'java']
可变对象函数内部可以进行操作,改变可变对象中存储的值,间接影响到函数外变量的引用
作用域和变量
-
在python中,作用域分为两个区域
- 函数外是一个作用域
- 函数内部是一个作用域
-
全局变量:将变量定义在函数外
-
局部变量:将变量定义在函数内部
-
全局变量和局部变量使用特点
- 局部作用域中可以使用到全局变量【可以使用函数外部定义的变量】
- 函数与函数内部的作用域是相互独立的,不能互相调用函数内部创建的局部变量
if 1==1: a=10 print(f"a:{a}") for i in range(11): pass print(f"i:{i}")
-
python中提供了一个关键字 global, 在函数内部定义一个全局变量,出了函数也是可以被访问到 的。
def fun1(): global a a = 100 fun1() print(a)
注意:如果函数内部有变量被global修饰,需要先调用该函数,让内存中出现这个变量,后 续才能去使用。
进阶:函数名也可以当作一个变量使用
-
用法场景1:
def fun1(): print("好好学习,天天向上!") fun2 = fun1 fun2()
赋值的时候,注意有没有小括号,方法名直接赋值,相当于给函数另起了一个名字;如果加了小括 号,相当于调用函数的结果赋值。
-
用法场景2:变量可以存储在容易中,比如列表
def fun1(): print("鹅鹅鹅") def fun2(): print("曲项向天歌") def fun3(): print("白毛浮绿水") def fun4(): print("红掌拨清波") fun_list = [fun1, fun2, fun3, fun4] flag = input("请输入开始:") for i in fun_list: i()
-
用法场景:将函数作为返回值使用
def fun1(): print("鹅鹅鹅") def fun2(): print("曲项向天歌") def fun3(): print("白毛浮绿水") def fun4(): print("红掌拨清波") fun_list = [fun1, fun2, fun3, fun4] def show1(): for i in fun_list: i() def function1(): return show1 res1 = function1() res1()
-
用法场景4:将函数作为参数传递
def fun1(): print("鹅鹅鹅") def fun2(): print("曲项向天歌") def fun3(): print("白毛浮绿水") def fun4(): print("红掌拨清波") fun_list = [fun1, fun2, fun3, fun4] def show1(): for i in fun_list: i() def function1(s): s() function1(show1)
作业:登录注册的案例
- main函数要有,用户自己选择要做的功能,根据选择调用不同的函数
- 用户注册的信息需要使用一个文件存储,登录需要判断用户是否存在,密码是否正确
- 注册的时候,需要发送邮件
函数和模块
在python开发中,我们需要利用python语言完成现实生活中的场景,python提供了许多内置的函 数和模块给我们使用,完善我们的程序,比如time,random等等模块,如果我们还想要学习其他 的模块,就需要使用第三方模块了。所以在python开发行业中,我们将python开发程序员称之为 调包侠。
内置函数
-
数学类函数
-
abs() 求绝对值
n = -12 print(abs(n))
-
sum() 求和 字符串类型的元素不行
list1 = [11,22,33,44,55] res1 = sum(list1) print(res1)
-
divmod() 传入两个数值,前一个除以后一个,得到两个值:一个商,一个是余数
s, y = divmod(16, 5) print(s) print(y)
-
round() 四舍五入
n = 12.765 print(round(n,2)) # 12.77
-
pow 求幂次方
print(pow(2,3))
-
-
聚合类函数
-
max() 求最大值
list1 = [123,53,225,1123,52,5,3,14] res1 = max(list1) print(res1)
-
min 求最小值
list1 = [123,53,225,1123,52,5,3,14] res1 = min(list1) print(res1)
-
all 判断一个列表中是否出现一个False
# 只要存在一个元素转bool类型结果是False,all()的结果就是False list1 = [12,45,124,'','hello',12.34] print(all(list1))
-
any 判断一个列表中是否出现一个True
# 只要存在一个元素转bool类型结果是True,all()的结果就是True list1 = [12,45,124,'','hello',12.34] print(any(list1))
-
-
和进制相关的函数
-
二进制
bin() 将十进制的值转二进制\
print(bin(136)) # 0b10001000
int() 将某一种进制转10进制
print(int('0b10001000',2))
-
八进制
oct() 将十进制转八进制
print(oct(136)) # 0o210
-
十进制
整数默认都是十进制
-
十六进制
hex() 将十进制转16进制
print(hex(136)) # 0x88
练习:将ip地址先转二进制,再转回十进制【点分十进制法】
-
-
字符类函数
-
ord() 将一个字符转成ASCII码数值
-
‘0’ - 48
-
‘A’ - 65
-
‘a’ - 97
print(ord('0')) print(ord('A')) print(ord('a'))
-
-
chr() 将数值转成对应的ASCII码字符
print(chr(97))
-
-
类型转换相关函数
-
int()
-
str()
-
bool()
-
list()
-
dict()
-
tuple()
-
set()
-
bytes()
-
float()
# s1 = '中国' # b1 = s1.encode('UTF-8') # print(b1, type(b1)) b2 = bytes('中国','UTF-8') print(b2)
-
-
获取输出类函数
-
input()
-
print()
-
len()
-
open()
-
获取索引和元素
list1 = [1,2,3,4] for i,j in enumerate(list1): print(i,j)
-
id() 获取对象的地址值
-
callable() 判断一个变量是否是一个函数
list1 = [1,2,3,4] def fun1(): pass print(callable(fun1))
-
sorted() 排序
list1 = [34,12,5,12,344,53] print(f"list1:{list1}") list2 = sorted(list1) print(f"list1:{list2}") # list1:[5, 12, 12, 34, 53, 344]
自定义排序依据:
list1 = ['小虎:1007', '黄沪生:1009', '查镕贤:1001', '黄涛:1004', '方直:1002'] def fun1(e): return int(e.split(':')[1]) list2 = sorted(list1, key=fun1) print(f"list1:{list2}")
-
zip() 将两个序列中的元素一一对应
list1 = [1001, 1002, 1003, 1004, 1005] list2 = ['小虎', '黄沪生', '查镕贤', '黄涛', '方直'] for i,j in zip(list1,list2): print(f"学号:{i}, 姓名:{j}")
-
函数生成式
python中提供了一个关键字可以让我们在函数中使用 yield
def fun1():
yield 1
yield 2
yield 3
yield 4
yield 5
res1 = fun1()
print("hello world")
print(res1.__next__())
# 若干行代码后
print(res1.__next__())
有yield关键字的函数,结果是可以使用for循环的
def fun1():
print("hello 1")
yield 1
print("hello 2")
yield 2
print("hello 3")
yield 3
print("hello 4")
yield 4
print("hello 5")
yield 5
res1 = fun1()
for i in res1:
print(i)
print("-----------")
def fun1():
for i in range(1,11):
yield i
res1 = fun1()
for i2 in res1:
print(i2)
print("-----------")
模块
简单理解为就是一个.py后缀的一个文件
- 内置模块
- 第三方模块
- 自定义模块
模块导入的方式
-
import 直接将一个模块导入进来
import day06.utils.login_tools as t # 导入login_tools模块并起了一个别名叫做t
-
from xxx import xxx 从一个模块中,导入具体的工具
from lxml import etree
from day06.utils.login_tools import rand_yzm # 从模块login_tools中导入某一个函数
from day06.utils.login_tools import rand_yzm,send_email # 一次从模块中导入多个
from day06.utils.login_tools import * # 导入模块中所有的内容
from day06.utils.login_tools import rand_yzm as t1 # 导入模块中的函数并重新命名
-
注意: 切记,自己定义的模块名不要和内置或第三方的模块名重名!!!!!
内置模块 random学习
- 理论上,python模块中,任意一个地方都可以进行导入
- 但是规范上,我们开发潜规则上,将导入的部分放在python模块文件最开始的位置编写
-
randint() 随机生成一个范围内的整数
n = random.randint(1000,2000) print(n)
-
uniform() 随机生成一个范围内的小数
n = round(random.uniform(0, 1),2) print(n)
-
choice() 随机抽取列表中的一个元素
res1 = random.choice(list1) print(res1) print(list1)
-
sample() 随机抽取列表中若干个元素
list1 = ['张三','李四','王五','赵六','王麻子','小李子'] # res1 = random.choice(list1) # print(res1) # print(list1) n = random.sample(list1,2) print(n, type(n))
-
shuffle() 随机打乱列表中的元素
list1 = ['张三','李四','王五','赵六','王麻子','小李子'] # res1 = random.choice(list1) # print(res1) # print(list1) # n = random.sample(list1,2) # print(n, type(n)) random.shuffle(list1) print(f"list1:{list1}")
练习:使用random模块以及之前学习的知识,编写一个抽奖程序。
# 使用random模块以及之前学习的知识,编写一个抽奖程序。
list1 = ['小虎', '张阳', '黄涛', '方一', '杨东', '黄生', '查贤']
jiang_xiang = [
('一等奖', 1, 'mate70 pro'),
('二等奖', 1, '小米手环'),
('三等奖', 2, '按摩仪'),
('四等奖', 2, '京东购物卡')
]
def chou_jiang(l1):
for i, num, goods in jiang_xiang:
print(f"正在抽取{i}".center(50, '-'))
name_list = random.sample(l1,num)
# 将中奖名单从原名单中删除
for n in name_list:
if n in l1:
l1.remove(n)
info = f"恭喜{','.join(name_list)} 中得{i}!!!!"
yield info
res1 = chou_jiang(list1)
input("开始抽一等奖....按下回车开始!")
print(res1.__next__())
input("开始抽二等奖....按下回车开始!")
print(res1.__next__())
input("开始抽三等奖....按下回车开始!")
print(res1.__next__())
input("开始抽四等奖....按下回车开始!")
print(res1.__next__())