- 函数概念
写了一段代码,实现了某个小功能,然后把这些代码集中到一块,起一个名字,下一次就可以根据这个名字再次使用这个代码块
方便代码的重用
分解任务,简化程序逻辑
使得代码更加模块化 - 函数的基本使用
def 函数名():
函数体
# 方便代码重用、分解任务简化程序逻辑、使代码更加模块化
# 内建函数、三方函数、自定义函数
# 简单使用
def test():
print(2)
print(4)
print(6)
print(19)
test()
- 函数的参数
单个参数
# 单一参数
# 当我们需要动态的调整函数体某一个处理信息可以以参数的形式接收到相关数据
# 参数名称为形参 在调用函数的时候,传递的真实数据为实参
def test1(num):
print(num)
print(num ** 2)
print(num ** 3)
print(num ** 4)
test1(5)
多个参数
# 多个参数
def sum(num1, num2):
print(num1)
print(num2)
result = num1 + num2
print(result)
sum(3, 6)
# 指明形参名称
sum(num2=34, num1=12)
# 参数为元组t,元组不可变
def sum(t):
result = 0
print(t, type(t))
for v in t:
result += v
sum((2, 3, 43, 23, 2))
不定长参数
# 不定长参数 方式一
# def 函数名(* args) 代表接收到的是一个元组
# 函数体中可以直接以元组的方式使用该参数
# 调用函数的方式:函数名(参数1, 参数2, ....)
def sum(*t):
result = 0
print(t, type(t))
for v in t:
result += v
sum(2, 3, 43, 23, 2) # 不使用括号
# 不定长参数 方式二
# def 函数名(**dic) 字典
# 函数体中可以直接以元字典的方式使用该参数
# 调用函数的方式:函数名(参数名称1=参数1, 参数名称2=参数2, ....)
def sum(**kwargs):
result = 0
print(kwargs, type(kwargs)) # {'age': 12, 'name': 'yl'} <class 'dict'>
for k in kwargs:
print(k)
sum(name="yl", age=12) # 不使用括号在这里插入代码片
# 函数的装包:把传递的参数包装成一个集合
# 函数的拆包:把集合参数再次分解成单独的个体
def Mysum(a, b, c, d):
print(a + b + c + d)
def test(*args):
print(args) # 整体装包:(1, 2, 3, 2)
print(*args) # 拆包1 2 3 2
Mysum(*args) # 参数传递四个
Mysum(args[0], args[1], args[2], args[3])
test(1, 2, 3, 2)
def mySum(a, b): # 参数名称只能与拆包对应
print(a)
print(b)
def Test(**kwargs):
print(kwargs)
# 拆包操作b
# 应该使用两个星星拆包
# print(**kwargs) 无法直接打印
mySum(**kwargs) # 拆出来是a=22,b=43 两个参数
Test(a=22, b=43)
缺省参数
result = sorted([1, 2, 3, 45, 32, 32], reverse=True)
print(result)
def hit(somebody="豆豆"):
print("我想打",somebody)
hit()
参数注意
# 参数注意事项
# 值传递:传递过来的是数据得一个副本,修改副本对原件没有任何影响
# 传的是20,根据20重新开辟空间存放20,把20的地址赋给函数num,在函数体内部修改num不影响b
# 引用传递:传递过来的是变量的地址,通过地址是操作同一份原件
# 在python中只有引用传递
# 如果数据类型是可变类型则可以改变原件反之,不可以改变原件
def change(num):
print(id(num)) # b和num的内存地址相同
num = 666 # num不可变无法修改,重新开辟空间666的id给num
print(num)
print(id(num))
b = 20 # b放着开辟的内存空间地址id
print(id(b)) # 1562334592
change(20)
print(b) # 20
def change(num):
print(id(num)) # b和num的内存地址相同
num.append(666) # num可变,b的值也发生了变化
print(num)
print(id(num))
b = [1, 2, 3] # b放着开辟的内存空间地址id
print(id(b))
change(b)
print(b) # [1, 2, 3, 666]
- 函数的返回值
# 函数的返回值
# 通过函数处理好数据之后拿到处理结果
def mySum(a, b=1):
"""
计算两个数据求和和求差
:param a: 数值1,数值类型,不可选,没有默认值
:param b: 数值1,数值类型,可选,默认值:1
:return: 返回的值是计算结果,元组:(和,差)
"""
result = a + b
cha = a - b
return result, cha # return后面的代码不会被执行
res = mySum(2, 4)
print(res) # (6, -2)
print(res[0])
print(res[1])
re, di = mySum(34, 54)
print(re) # 88
print(di) # -20
- 函数的使用描述
# 函数的使用描述
# 当我们编写三方函数的时候,为了方便他人使用,需要描述清楚我们所写的函数功能以及使用方式等信息
# 直接在函数体最上面添加三个双引号对注释
# def 函数名():
# '''这里写帮助信息'''
# 1.函数的功能
# 2.参数:含义、类型、是否省略、默认值
# 3.返回值:含义、类型
- 偏函数
当我们写一个参数比较多的函数时,有些参数大部分场景下都是某个固定值,为了简化使用,可以创建一个新函数,指定我们要使用函数的某个参数值为某个固定值
# 1、偏函数
def test(a, b, c, d=1):
print(a + b + c + d)
def test2(a, b, c, d=2):
test(a, b, c, d)
test2(1, 2, 3)
import functools
newfunc = functools.partial(test, c=5)
print(newfunc, type(newfunc))
newfunc(1, 2) # 9
# 使用场景
numStr = "100010"
result = int(numStr, base=2) # 以二进制转成整型数据
print(result) # 34
# 在往后的一段时间内,我都需要把一个二进制的字符串转换成对应十进制数据
int2 = functools.partial(int, base=2)
print(int2(numStr)) # 34
- 高阶函数
# 2、高阶函数
# 函数本身也可以作为数据传递给另外一个变量
# sorted函数就是一个高级函数
# 函数的参数接受的是另外一个函数
rest3 = test
rest3(1, 2, 3)
l = [{"name": "yl", "age": 13}, {"name": "yl", "age": 14}, {"name": "yl", "age": 11}]
def getKey(x):
return x["age"]
result = sorted(l, key=getKey)
print(result)
# 应用场景
def caculate(num1, num2, caculateFunc):
print(caculateFunc(num1, num2))
def sum(a, b):
return a + b
def jianfa(a, b):
return a - b
caculate(4, 6, sum)
- 返回函数
# 3、返回函数
def getFunc(flag):
# 1.再次定义几个函数
def sum(a, b):
return a + b
def jian(a, b):
return a - b
# 根据不同的flag值,返回不同的操作函数
if flag == "+":
return sum
elif flag == "-":
return jian
res = getFunc("-")
print(res, type(res)) # <function getFunc.<locals>.jian at 0x000001F0900C20D0> <class 'function'>
re = res(23, 4)
print(re) # 19
- 匿名函数
# 4、匿名函数“lambda函数” 没有名字的函数
result = (lambda x, y: x + y)(1, 2)
print(result) # 3
newFunc = lambda x, y: x + y
print(newFunc(2, 3)) # 5
l = [{"name": "yl", "age": 13}, {"name": "yl", "age": 14}, {"name": "yl", "age": 11}]
# def getKey(x):
# return x["age"]
result = sorted(l, key=lambda x: x["age"])
print(result)
- 闭包
# 5、闭包
# 在函数的嵌套前提下,内层函数引用了外层函数的变量 外层函数又把内层函数当作返回值进行返回
# 内层函数+外层变量=闭包
def test():
a = 10
def test2():
print(a)
return test2
newFunc = test() # newFunc = test2
newFunc() # print(a) 10
# 应用场景:外层函数,根据不同的参数来生成不同作用功能的函数
# 根据不同的配置信息,生成不同的分割线函数
def line_config(content, length):
def line():
print("-"*(length // 2) + content + "-"*(length // 2) )
return line
line1 = line_config("闭包", 20)
# ----------闭包----------
line1()
line2 = line_config("xxx", 50)
# -------------------------xxx-------------------------
line2()
# 注意事项
# 闭包中,修改引用的外层变量需要使用nonlocal变量声明 否则当作是闭包内新定义的变量
def test():
a = 10
def test2():
nonlocal a # 全局可以修改外部变量
a = 999
print(a)
print(a)
test2()
print(a)
return test2
result = test()
result() # 10 999 999 999
# 当函数被调用的时候才会真正确定对应的值到底是什么,之前们都是以普通的变量标识存在
def test():
a = 10
def test2():
print(a)
a = 2
return test2
result = test()
result() # 2
def test():
funcs = []
for i in fange(1,4):
def test2():
print(i)
funcs.append(test2)
return funcs
result = test()
# i =3
result[0]() # 3
result[1]() # 3
result[2]() # 3
- 装饰器
# 装饰器
# 作用:在函数名和函数体不改变的前提下给一个函数附加一些额外的代码
# 装饰器的执行时间是立即执行
# 定义两个功能函数
def fss():
print("发说说")
def ftp():
print("发图片")
# 相关逻辑代码
btnIndex = 1
if btnIndex == 1:
fss()
else:
ftp()
# 发说说和图片,必须有前提就是用户登录
# 登录验证操作
# 方式一:在逻辑代码中增加,但是逻辑代码多,每一份逻辑代码在调用具体功能函数之前都有需要去验证
# 代码冗余、复用性差、维护性也差
btnIndex = 1
if btnIndex == 1:
print("登录验证")
fss()
else:
print("登录验证")
ftp()
# 方式二:功能函数里面修改,方便代码的重用
# 定义两个功能函数
def fss():
print("登录验证")
print("发说说")
def ftp():
print("登录验证")
print("发图片")
# 相关逻辑代码
btnIndex = 1
if btnIndex == 1:
fss()
else:
ftp()
# 再次优化
def fss():
checkLogin()
print("发说说")
def ftp():
checkLogin()
print("发图片")
def checkLogin():
print("登录验证")
# 相关逻辑代码
btnIndex = 1
if btnIndex == 1:
fss()
else:
ftp()
# 开放封闭原则(写好的代码尽可能不要修改 如果想要新增功能在源代码的基础之上单独进行扩展)、单一职责
def checkLogin(func):
def inner():
print("登录验证")
func()
return inner
def fss():
print("发说说")
fss = checkLogin(fss)
def ftp():
print("发图片")
ftp = checkLogin(ftp)
btnIndex = 1
if btnIndex == 1:
fss()
else:
ftp()
# 语法糖 写法
@checkLogin
def ftp():
print("发发图片")
@checkLogin
def fss():
print("发发说说")
btnIndex = 1
if btnIndex == 1:
fss()
else:
ftp()
# 装饰器执行时间:立即执行
# 给发说说增加一些额外功能
# 1、函数名字不能改变
# 2、函数体内代码不能发生改变
def check(func):
def inner():
print("登录验证操纵....")
func()
return inner
def fss():
print("发发说说")
fss = check(fss) # fss=inner
fss()
@check
def fss():
print("发发说说")
fss()
# 装饰器叠加
# 从上到下去装饰
# 从下到上去执行
def zhshiqi_line(func):
def inner():
print("----------")
func()
return inner
def zhshiqi_star(func):
def inner():
print("*************")
func()
return inner
@zhshiqi_star
@zhshiqi_line
def print_content():
print("dhsfdfsfd")
print_content()
# 有参数的函数装饰
# 不定长参数 通用
def zsq(func):
def inner(*args, **kwargs):
print("_"*30)
print(args, kwargs)
func(*args, **kwargs)
return inner
@zsq # pnum = zsq(pnum)
def pnum(num, num2, num3):
print(num, num2, num3)
pnum(123, 43, num3=999)
# 有返回值函数 无论什么场景,保证函数返回值一致
# inner与被装饰函数保持一致
def zsq(func):
def inner(*args, **kwargs):
print("_"*30)
res = func(*args, **kwargs)
return res
return inner
@zsq
def pnum(num, num2, num3):
return num + num2 + num3
result = pnum(123, 43, num3=999)
print(result)
# 带有参数的装饰器
def getzsq(char):
def zsq(func):
def inner():
print(char * 20)
func()
return inner
return zsq
@getzsq("*") # f1 = zsq(f1)
def f1():
print("666")
f1()
- 递归函数
函数A内部 继续调用函数A
传递 回归 - 生成器函数
是一个特殊的迭代器(迭代器抽象层次更高)
迭代器特性:惰性计算数据,节省内存 记录状态,next()函数访问下一个状态 具备可迭代的特性(for in)
# 生成器:是一种特殊迭代器
# next()函数访问下一个状态
# 创建方式一:生成器表达式 把列表推导式[]修改成() l=[i for i in range(0, 100) if i % 2 == 0] 全部生成,时间卡很久
# 用到才分配取用
l = (i for i in range(0, 100) if i % 2 == 0)
print(next(l)) # 0
print(next(l)) # 2
print(l.__next__()) # 同上 4
# 创建方式二:借助生成器函数(包含yield语句),函数执行结果就是生成器
# yield可以阻断当前函数执行
# 使用next()函数会让函数执行,当遇到下一个函数会暂停
def test():
print("xxx")
yield 1 # 一个一个状态值 next第一次状态值
print("a")
yield 2
print("b")
g = test() # 不执行,只是产生生成器
print(next(g)) # xxx 1
print(next(g)) # a 2
# send()方法
# send方法有一个参数,指定上一次被挂起的yield语句的返回值
# 相比__next__() 可以额外的给yield语句传值
# 注意第一次调用 g.send(None)
def test():
res1 = yield 1 # oooo赋值给res1
print(res1)
res2 = yield 2
print(res2)
g = test()
print(g.send(None)) # 第一次调用必须None 1
print(g.send("oooo")) # 给上一次yield语句传值 print(res1)=oooo 2
# close()方法
def test():
yield 1
print("a")
yield 2
print("b")
yield 3
print("")
g = test()
print(g.send(None)) # 第一次调用必须None 1
print(g.send(None)) # 第一次调用必须None a 2
g.close()
# print(g.__next__()) # 报错
# 注意事项
# 注意:碰到return 直接终止,抛出StopIteration异常提示
# 生成器只会遍历一次
def test():
yield 1
print("a")
# return 10 # 异常
yield 2
print("b")
yield 3
print("c")
return 10
g = test()
print(g.send(None)) # 第一次调用必须None
print(g.send(None)) # 第一次调用必须None
print(g.send(None)) # 第一次调用必须None
def test():
yield 1
print("a")
yield 2
print("b")
yield 3
print("c")
g = test()
for i in g:
print(i)
print("hhhh")
for i in g: # 只会迭代一次
print(i)
- 函数作用域
变量的作用范围 可操作范围
python是静态作用域,变量的作用域源于它在代码中的位置
在不同的位置,可能有不同的命名空间
命名空间是作用域的体现形式 不同的具体的操作范围
python-LEGB
L-函数内命令空间 当前整个函数范围
E-外部嵌套函数命名空间 闭包函数
G-全局命名空间 当前模块(文件)
B-内建模块命名空间 所有模块(文件)
注意:python中没有块级作用域 (if while for后的代码)
按照L-E-G-B顺序进行查找