python第五章——函数
- 第五章——函数
- 5.1函数概述
- 5.2 函数进阶
- 下面部分之后再写
- 5.3函数习题(一些小例子)
第五章——函数
5.1函数概述
快速入门:
#定义函数
def func():
print('123')
#调用函数
func()
加小括号是调用函数,不加小括号可能是进行值的传递
(1)函数定义:实现某一项特定功能的代码块
(2)函数分类:内置VS自定义
1)内置函数:Python解释器已经写好的函数,无需对象访问,直接调用即可
2)自定义函数:
定义方式:def关键字
调用方式:就是直接写函数名()
注意:函数只有被调用的时候,才会执行里面的代码块
3)函数参数:
形参:形式上的参数,你传递的是啥,我就是啥
实参:实际传过去的参数
def func(a,b): # a,b就是形参
print(a+b)
func('hello','world') # 拼接(实参)
func(100,566) # 运算(实参)
#helloworld
#666
注意:形参和实参要一一对应
4)函数返回值:
关键字:return
说明:所有函数都有返回值,函数是去执行一项功能,执行完必然有反馈
作用:将结果返回给调用处,并结束当前代码
总结:学习一个函数,首先要知道它的功能,其次就是要知道它的参数和返回值
Structure里显示定义的函数
两种加法器实现方式:
# 加法器1
def fun1(a,b):
print(a+b)
# 加法器2
def fun2(a,b):
return a+b # 更灵活
fun1(10,20)
sum = fun2(30,40)
print(sum)
python这么写会报错:
fun() # 调用动作必须在声明函数之后
def fun():
print('hello')
从函数中返回函数:
其实并不需要在一个函数里去执行另一个函数,我们也可以将其作为输出返回出来:
def greeting(name="hello"):
def hello():
return "now you are in the hello() function"
def world():
return "now you are in the world() function"
if name == "hello":
return hello
else:
return world
a = greeting()
print(a) # <function greeting.<locals>.hello at 0x0000018EE2AD7268>
print(a()) # now you are in the hello() function
b= greeting(name="world")
print(b()) # now you are in the world() function
print(greeting()()) # now you are in the hello() function
注:加小括号是调用函数,不加小括号是进行值的传递,为了在函数中执行另一个函数,在 if/else 语句中我们返回 hello 和 world,而不是 hello() 和 world()将函数作为参数传给另一个函数:
def world():
return "world!"
def hello(func):
print(func())
hello(world) # world!
练习题:
(1)定义一个函数,传递整数n,返回1-n之间的偶数和
(2)定义一个函数,使其能够将信息(数字密码)转成暗语输出
def fun1(n):
# 1.验证
import re
if not re.match(r'^-?\d+$',n):
return '非法输入'
# 2.求和
n = int(n) # 负数不会报错
# 3.负数(这里将负数正常处理了,实际上负数是不能这样做的)
step = 1 if n>=0 else -1
sum=0
for i in range(1,n+step,step):
if i%2==0:
sum+=i
return sum
num = input('请输入一个整数:')
print(fun1(num))
(2)定义一个函数,使其能够将信息(数字密码)转成暗语输出
暗语规则:19004546 -> IPOOYSYG
两种实现方式:
def fun2(sr):
dt = {'0':'O','1':'I','2':'Z','3':'E','4':'Y','5':'S','6':'G','7':'L','8':'B','9':'P'}
ans = ''
for i in sr:
ans += dt[i]
return ans
print(fun2("123456789"))
def filter(pwd): # 123
res = str(pwd)
if not res.isdecimal():
return '非法输入'
p = 'OIZEYSGLBP'
con = ''
for i in res: # 传进来的每一个数就是p的索引值
con += p[int(i)]
return con
pwd = input('请输入您的密码:')
print(filter(pwd))
5.2 函数进阶
1.pass空语句(用在测试):
# pass可以写在所有的冒号场景
# if/while/for/def/……
def fun1():
pass
2.关键字参数:
# 可以给形参和实参都设置关键字
def fun2(a='hello',b='world'): # 形参关键字
print(a+b)
fun2() # helloworld
fun2('123') # 123world
fun2('123','456') # 123456
# 实参关键字
fun2(a='python') # pythonworld
fun2(b='java') # hellojava
fun2(b='css',a='html') # htmlcss 关键字参数可以不按顺序
3.可变参数:
可变参数就是传入的参数个数是可变的,可以是1个、2个到任意个,还可以是0个
# 1)元组写法:
def fun3(*hobby):
print(hobby)
print('我的爱好是:')
for i in hobby:
print(i)
fun3('吃饭','睡觉','打游戏')
'''
('吃饭', '睡觉', '打游戏')
我的爱好是:
吃饭
睡觉
打游戏
'''
# 2)字典写法:
def fun4(**kwargs): # 两个星号表示空字典
print(kwargs)
# 关键字参数传值
fun4(id=10,user='root',pwd='123456') # 键不加引号 {'id': 10, 'user': 'root', 'pwd': '123456'}
# 直接传字典本身,必须加**
fun4(**{'a':10,'b':20}) # {'a': 10, 'b': 20}
关键字参数与可变参数的综合应用:
def fun5(a,b=10,*res):
print(a,b,res)
fun5('hello') # hello 10()
fun5('hello',123) # hello 123()
fun5('hello',123,456,789) # hello 123 (456, 789)
# a是普通参数放最左边,b是关键字参数,*res是可变参数(元组)必须放最后
# 接收任意参数(顺序)
def fun6(*args,**kwargs):
print(args)
print(kwargs)
fun6() # {}
'''
()
{}
'''
fun6(a='hello')
'''
()
{'a': 'hello'}
'''
fun6(1,2,3,4,5)
'''
(1, 2, 3, 4, 5)
{}
'''
fun6(1,2,3,b='xxx') # 注意顺序
'''
(1, 2, 3)
{'b': 'xxx'}
'''
可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple
而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict
4.作用域:
局部:函数内部
全局:函数外部
# 写法(1)
a = 10 # 全局
def fun():
a = 20 # 局部
fun()
print(a) # 10
# 写法(2)
b = 10
def fun2():
# 结论:当局部变量在当前作用域找不到的时候,向上寻找
# 注意:只能向上寻找,不能向下寻找
print(b)
fun2() # 10
# 写法(3)
# 需求:在函数内部修改全局变量
c = 10
def fun3():
global c #注意:一定要先声明
c = 20
fun3()
print(c) # 20
d = None # 相当于java或c中的声明变量且没赋值
def fun4():
global d
d = 10
fun4()
print(d) # 10
# 写法(4)
def fun5(pwd): # pwd形参(局部变量)
print(pwd)
pwd = '123456'
fun5(pwd) # 123456 pwd实参(全局变量)
推荐看一下其他人的文章以熟悉一下global()函数的其他用法
5.匿名函数:lambda表达式
说明:匿名就是函数没有名字
# 正常写法:
def fun(x,y):
return x+y
print(fun(1,3))
# 匿名写法
sum = lambda x,y:x+y
lambda表达式就是匿名函数,没有名称的函数,但返回的变量可以看成lambda的名称.正常调用函数的写法就是调用该变量的写法
说明:lambda只是一个表达式,不能单独存储,且函数体比直接def定义的函数要简单
说明:lambda主体是一个表达式,而不是一个代码块,所以在lambda表达式中封装的逻辑很有限
关于sort()排序:
(1)reverse场景
ls=[1,45,6,2,33,78,11]
ls.sort(reverse=True)
print(ls) # [78, 45, 33, 11, 6, 2, 1]
ls.sort(reverse=False)
print(ls) # [1, 2, 6, 11, 33, 45, 78]
(2)key的场景
def fun(con):
return int(re.search('\d+',con).group()) # 每一句开头的数字 group()用来提出分组截获的字符串,()用来分组
import re
sr = "12.一代天骄成吉思汗,只识弯弓射大雕。9.须晴日,看红装素裹,分外妖娆。2.千里冰封,5.惟余莽莽;3.万里雪飘,4.望长城内外,6.大河上下,1.北国风光,7.顿失滔滔。8.山舞银蛇,原驰蜡象,欲与天公试比高。11.惜秦皇汉武,略输文采;唐宗宋祖,稍逊风骚。13.俱往矣,数风流人物,还看今朝。10.江山如此多娇,引无数英雄竞折腰。"
ls = re.findall(r'\d+\D+',sr)
ls.sort(key=fun) # 注意:key是函数,不能写括号,写括号是调用
for i in ls:
i = re.findall(r'[\u4e00-\u9fa5]\D+', i) # 去掉序号
i = (' '.join(i)) # 去掉元组的括号
print(i)
(3)lambda表达式
import re
sr = "12.一代天骄成吉思汗,只识弯弓射大雕。9.须晴日,看红装素裹,分外妖娆。2.千里冰封,5.惟余莽莽;3.万里雪飘,4.望长城内外,6.大河上下,1.北国风光,7.顿失滔滔。8.山舞银蛇,原驰蜡象,欲与天公试比高。11.惜秦皇汉武,略输文采;唐宗宋祖,稍逊风骚。13.俱往矣,数风流人物,还看今朝。10.江山如此多娇,引无数英雄竞折腰。"
ls = re.findall(r'\d+\D+',sr)
ls.sort(key=lambda con:int(re.search('\d+',con).group()))
for i in ls:
i=re.findall(r'[\u4e00-\u9fa5]\D+',i) # 去掉序号
i=(' '.join(i)) # 去掉元组的括号
print(i)
lambda练习题:制作键为1-20的字典,值为50-100之间的随机分数
1)将字典随机打乱顺序(返回新字典){19:59,2:87,13:66,…}
2)按分数倒序构建新的字典{5:99,14:98,13:90,…}
from random import randint
from random import shuffle
def dt_shuffle(dt):
ls = list(dt.keys())
shuffle(ls)
return {key:dt[key] for key in ls}
def dt_sorted(dt):
# 思路:lambda表达式
# 思路:列表
# ls = []
# 1)for循环:[(1,'aa'),(2,'bb'),(3,'cc')]
# for key,value in dt.items():
# ls.append((key,value))
# 2)以上思路用一个zip函数即可实现:[(1,'aa'),(2,'bb'),(3,'cc')]
ls = list(zip(dt.keys(),dt.values()))
ls.sort(reverse=True,key=lambda tp:tp[1])
# 新字典
dt_new = {}
dt_new.update(ls)
return dt_new
if __name__ == '__main__':
# 1.制作字典
dt = {x:randint(50,100) for x in range(1,21)}
# 2.随机打乱
# sorted() 内置函数 sorted(ls,key=lambda i:randint(0,9999))
# shuffle() 随机数模块的方法
dt = dt_shuffle(dt) # 随机打乱
print(dt)
# 3.值的排序
dt = dt_sorted(dt)
print(dt)
6.冒号与箭头:Python3.5新功能
说明:参数中的冒号指参数的类型建议符,告诉开发人员希望传入的实参类型(不是强制,类型不符也不报错)
说明:函数后面的箭头指函数的返回值建议符,用来说明函数返回值
def func(x:int,y:int) -> int: # -> int表示建议返回值的类型是int
sum = x+y
return sum
res1 = func('hello','world')
res2 = func(123,456)
print(res1) # helloworld
print(res2) # 579
print(res1,res2) # helloworld 579
7.编程中的经典错误:列表引用问题
def fun(a,b):
a+=b
return a
# 1
x=1
y=2
print(fun(x,y)) # 3
print(x) # 1
print(y) # 2
# 2
x=(1,2)
y=(3,4)
print(fun(x,y)) # (1, 2, 3, 4)
print(x) # (1, 2)
print(y) # (3, 4)
# 3
x=[1,2]
y=[3,4]
print(fun(x,y)) # [1, 2, 3, 4]
print(x) # [1, 2, 3, 4] 变了,因为列表可变
print(y) # [3, 4]
# 结论:列表是引用传递(指针)在编程中应当注意此种问题,避免得到错误的列表
# 字典
def fun(dt):
dt['id']='hello'
return dt
dt = {'user':'root'}
print(fun(dt)) # {'user': 'root', 'id': 'hello'}
print(dt) # {'user': 'root', 'id': 'hello'}
#列表
def func(ls):
ls.append('hello')
return dt
dt={'user':'root'}
print(fun(dt)) # {'user': 'root', 'id': 'hello'}
print(dt) # {'user': 'root', 'id': 'hello'}
# 列表
def func(ls):
ls.append('hello')
return ls
ls = [1,2,3]
print(func(ls)) # [1, 2, 3, 'hello']
print(ls) # [1, 2, 3, 'hello']
8.函数递归:
递归就是自己调用自己
注意:递归的效率很低,只是有些场景下,递归写法很简单,代码可读性很高
注意:递归相当于死循环,必须有停止的点
# 嵌套调用(不是递归)
def fun1():
fun2()
def fun2():
fun3()
def fun3():
print('hello')
fun1() # hello
# 递归
import time
def func(i):
print('world')
i += 1
time.sleep(0.5)
if i<=3:
func(i)
func(0)
'''
world
world
world
world
'''
9.函数闭包
语法:函数的闭包,通俗地讲,就是函数内部再写一个函数
作用:可以保持程序上一次运行后的状态,然后继续执行
# 正常传参
def fun(res):
print(res)
# 闭包传参
def func():
num = 1
def add(n):
nonlocal num # nonlocal只能在内嵌函数中使用,使用前提是外部函数必须先声明nonlocal声明的变量
# nonlocal作用:内外共享一个变量
num += n
return num
return add # 返回的是内嵌函数的名字
# 调用
f = func()
print(f(1)) # 2
print(f(2)) # 4 把上一行注释掉再运行一遍的话,就输出3
print(f(3)) # 7 把上一行注释掉再运行一遍的话,就输出4
注意:闭包必须满足一下3个条件,才会产生闭包
1)必须是函数嵌套
2)内部函数必须引用外部函数里的变量
3)外部函数必须返回内嵌函数
10.函数装饰器:
函数装饰器这里仅仅简单的介绍。详细了解可以看看作者“攻城狮白玉”的文章,写的非常透彻
说明:装饰器就是用于拓展原来函数功能的一种函数
实质:装饰器的本质就是闭包函数
场景:在不改变原函数名的情况下,给被装饰对象添加新的功能
原则:函数装饰器有开放封闭原则,即开放是对扩展进行开放,封闭是对修改封闭
1)不改变被装饰对象的源代码(因为容易影响之前的功能)
2)不改变被装饰对象的调用方式(因为调用处多的时候很麻烦)
需求:想要给func升级:获取计算时间
def func():
sum = 0
for i in range(101):
sum+=i
print(sum)
func()
需求实现1:改变源码
import time
def func():
start = time.time()
sum = 0
for i in range(101):
sum += i
time.sleep(0.01)
print(sum)
end = time.time()
print(end-start)
func()
需求实现2:改变调用方式
import time
def func():
sum = 0
for i in range(101):
time.sleep(0.01)
sum += i
print(sum)
def fn():
start = time.time()
func()
end = time.time()
print(end-start)
fn()
需求实现3:闭包写法(避免违背开闭原则)
import time
def func():
sum = 0
for i in range(101):
time.sleep(0.01)
sum += i
print(sum)
def timer(fn): # fn就是函数名
def test():
start = time.time()
fn() # 调用
end = time.time()
print(end-start)
return test
a=timer(func)
a()
需求实现4:装饰器写法
import time
# 装饰器构建
def timer(fn): # fn就是函数名
def test():
start = time.time()
fn() # 调用
end = time.time()
print(end-start)
return test
# 装饰器写法
@timer # 注解
def func():
sum = 0
for i in range(101):
time.sleep(0.01)
sum+=i
print(sum)
# 调用
func()
多个装饰器
# 装饰器1
def fn01(func):
def inner():
print('fn01')
func()
print('fn01')
return inner
def fn02(func):
def inner():
print('fn02')
func()
print('fn02')
return inner
def fn03(func):
def inner():
print('fn03')
func()
print('fn03')
return inner
@fn03
@fn02
@fn01
def func():
print('hello123')
# 调用
func()
# 输出结果:
#fn03
#fn02
#fn01
#hello123
#fn01
#fn02
#fn03
下面部分之后再写
装饰器习题:
(1)为一个列表推导式函数增加一个验证纯数字的功能
(2)星座问题
5.3函数习题(一些小例子)