Python基础编程之函数
- 概念
- 基本使用
- 参数
- 单个参数
- 多个参数
- 不定长参数
- 缺省参数
- 注意事项
- 返回值
- 使用描述
- 偏函数
- 高阶函数
- 返回函数
- 匿名函数
- 闭包
- 装饰器
- 生成器
- 递归函数
- 函数的作用域
概念
写了一段代码实现了某个小功能:然后把这些代码集中到一块,起一个名字;下一次就可以根据这个名字再次使用这个代码块,这就是函数写了一段代码实现了某个小功能:然后把这些代码集中到一块,起一个名字;下一次就可以根据这个名字再次使用这个代码块,这就是函数。
作用:方便代码的重用;分解任务,简化程序逻辑;使代码更加模块化。
函数分为:内建函数;三方函数;自定义函数。
基本使用
简单定义:
def 函数名():
函数体
函数调用:
函数名()
参数
单个参数
场景:需要动态的调整函数体中某一个处理信息,则可以以参数的形式接收到相关数据。
def 函数名(参数名称):
函数体(函数体中,可以直接以变量的方式使用该参数)
# 调用
函数体(参数值)
形参和实参的概念:
上述函数定义中,参数名称即为形参;
在调用函数的时候,传递的真实数据,即为实参。
def test(num): # num是形参
print(num**1)
print(num**2)
print(num**3)
test(5) # 5是实参
输出:
5
25
125
多个参数
场景:需要动态的调整函数体中多个处理信息时,则可以以 逗号 做分割,接收多个参数。
def 函数名(参数名称1,参数名称2):
函数体(函数体中,可以直接以变量的方式使用所有参数)
调用方式:
- 函数名(参数1, 参数2,参数3…)
形参和实参:一一对应。 - 函数名(参数名称1=参数1,参数名称n = 参数n…)
可以指明形参名称称为“关键字参数”,不需要严格按照顺序。
def Sum(num1, num2):
print(num1,num2)
print(num1 + num2)
Sum(4,5)
Sum(num2=5,num1=4)
输出:
不定长参数
场景:如果函数体中,需要处理的数据,不确定长度,则可以以不定长参数的方式接收数据。
方式一:
def 函数名(*args):
函数体(元组,函数体中,可以直接以元组变量的方式使用该参数)
函数名(参数1,参数2,参数3...)
def mySum(*t):
print(t, type(t))
result = 0
for v in t:
print(v)
result += v
print(result)
mySum(4,5,6,7)
输出:
方式二:
def 函数名(**dic):
函数体(字典,函数体中,可以直接以字典变量的方式使用该参数)
函数名(参数名1=参数1,参数名2=参数2,参数名3=参数3...)
def Sum(**kwargs):
print(kwargs,type(kwargs))
Sum(name="sz",age=12)
输出:
装包与拆包:
把传递的参数,包装成一个集合,称之为“装包”。
把集合参数,再次分解成单独的个体,称之为”拆包”。
def test(*args):
print(args)
# 拆包
print(*args)
test(1,2,3,4)
输出:
(1, 2, 3, 4)
1 2 3 4
# 根据此,我们就能用一个输入来直接进行多个参数的输入
def mySum(a,b,c,d):
print(a+b+c+d)
def test(*args):
print(args)
#拆包
print(*args)
#mySum((1,2,3,4))
#mySum(args[0],args[1],args[2], args[3])
mySum(*args)
test(1,2,3,4)
输出:
def mySum(a,b):
print(a)
print(b)
def test(**kwargs):
print(kwargs)
# 拆包操作
# 应该使用 **进行拆包操作
# print(**kwargs)
mySum(**kwargs)
test(a=1,b=2)
输出:
缺省参数
当我们使用一个函数的时候,如果大多数情况下,使用的某个数据是一个固定值,或者属于主功能之外的小功能实现:则可以使用默认值 · 这种参数,称为"缺省参数”。
def 函数名(变量名1=默认值1,变量名2=默认值2):
函数体(函数体中,即使外界没有传递指定变量,也可以使用,只不过值是给定的默认值)
函数名(变量1,变量2) # 此处如果是缺省参数,则可以不填写
def hit(somebody="DB"): # 若不写somebody="DB",只写somebody且函数内不写参数,则会报错
print("我想打",somebody)
hit()
输出:
我想打 DB
注意事项
值传递:是指传递过来的,是一个数据的副本;修改副本,对原件没有任何影响。
引用传递:是指传递过来的,是一个变量的地址通过地址,可以操作同一份原件。
但在Python当中,只有引用传递(地址传递)。
但是如果数据类型是可变类型,则可以改变原件;如果数据类型是不可变类型(在Python中不可变的数据类型有3种,分别是整型、字符串和元组),则不可以改变原件。
- 首先我们看看不可变的:
发现他的地址是不一样的
- 来看看可变的:
发现他的地址是一样的
返回值
场景:当我们通过某个函数,处理好数据之后,想要拿到处理的结果。
# 语法
def 函数():
函数体
return 数据
注意:return的后续代码不会被执行;只能返回一次;如果想要返回多个数据,可先把多个数据包装成一个集合,整体返回。
示例:
# 对于单个返回
def mySum(a,b):
result = a+b
return result
res = mySum(6,7)
print(res)
# 对于多个返回
def caculate(a,b):
he = a+b
cha = a-b
return (he,cha)
he,cha=caculate(6,7)
res = caculate(6,7)
print(res[0])
print(res[1])
print(he,cha)
输出:
13
13
-1
13 -1
使用描述
当我们编写三方函数,为了方便他人使用,就需要描述清楚我们所写的函数功能以及使用方式等信息。
# 定义格式
''' 直接在函数体的最上面,添加三个双引号对注释 '''
def 函数():
"""
这里写帮助信息
"""
查看函数使用文档:help(函数)
一般的函数描述包括:
- 函数的功能
- 参数(含义、类型、是否可以省略、默认值)
- 返回值(含义、类型)
示例:
偏函数
概念:当我们写一个参数比较多的函数时,如果有些参数,大部分场景下都是某一个固定值,那么为了简化使用,就可以创建一个新函数,指定我们要使用的函数的某个参数,为某个固定的值,这个新函数就是“偏函数”。
# 法1:自己写一个新的
def test(a,b,c,d=1):
print(a+b+c+d)
def test1(a,b,c=1,d=4): # 偏函数
test(a,b,c,d)
test1(1,2)
# 法2:利用newFunc = functools.partial(函数, 特定参数=偏值)
import functools
newFunc = functools.partial(test,c=1) # 指定函数,和什么值做偏值
print(newFunc, type(newFunc))
newFunc(1,2)
输出:
示例:
# 利用偏函数
# 在往后的一段时间内,我都需要把一个二进制的字符串,转换成为对应的十进制数据
numStr='100100'
import functools
int2 = functools.partial(int, base=2)
print(int2(numStr))
输出:36
高阶函数
概念:当一个函数A的参数,接收的又是另一个函数时,则把这个函数A或为是“高阶函数”。
# 函数本身可以作为数据传递给另一个变量
def test(a,b):
print(a + b)
print(test)
print(id((test)))
test2 = test
test2(1,2)
输出:
# 拿sorted举例
l=[{"name":"sz","age":18},{"name": "sz2", "age": 19},{"name":"SZ3","age" : 18.5}]
def getKey(x):
return x["age"]
result = sorted(l, key=getKey) # key指定一个函数
print(result)
输出:
[{‘name’: ‘sz’, ‘age’: 18}, {‘name’: ‘SZ3’, ‘age’: 18.5}, {‘name’: ‘sz2’, ‘age’: 19}]
示例:
计算两个动态数据(利用回调函数)
# caculate称为高阶函数
def caculate(num1, num2, caculateFunc): # 参数caculateFunc接受的就是另外一个函数
result = caculateFunc(num1, num2)
print(result)
def sum(a, b):
return a + b
def jianfa(a, b):
return a-b
caculate(6,2,jianfa)
输出:4
返回函数
概念:是指一个函数内部,它返回的数据是另外一个函数,把这样的操作成为“返回函数”。
# 根据不同参数,获取不同操作,做不同计算
def getFunc(flag):
# 1.再次定义几个函数
def sum(a,b,c):
return a+b+c
def jian(a,b,c):
return a-b-c
# 2.根据不同的flag值,来返回不同的操作函数
if flag == "+":
return sum
elif flag == "-":
return jian
result = getFunc("+")
# print(result, type(result))
res =result(1,3,5)
print(res)
输出:9
匿名函数
概念:称为lambda函数,指没有名字的函数。
语法结构:lambda 参数1, 参数2… :表达式
注意事项:只能写一个表达式,并且不能直接return;表达式的结果就是返回值;只适用一些简单的操作。
# 1.
result=(lambda x,y:x+y)(1,2)
print(result)
# 2.
newFunc = lambda x,y:x+y
print(newFunc(4,5))
# 3.
l=[{"name":"sz","age":18},{"name": "sz2", "age": 19},{"name":"SZ3","age" : 18.5}]
result = sorted(l, key= lambda x: x["age"])
print(result)
输出:
闭包
概念:在函数嵌套的前提下,内层函数引用了外层函数的变量(包括参数),外层函数又把内层函数当做返回值进行返回。这个内层函数+所引用的外层变量,称为"闭包。
# 函数定义理解
def test():
a = 10
def test2():
print(a)
return test2
newFunc = test()
newFunc()
作用:外层函数,根据不同的参数,来生成不同作用功能的函数。
示例:
# 根据配置信息,生成不同的分割线函数
def line_config(content, length):
def line():
print("-"*(length //2)+ content + "-"*(length // 2))
return line
line1 = line_config("闭包",40)
line1()
输出:
注意事项:
- 闭包中,如果要修改引用的外层变量需要使用nonlocal变量声明,否则当做是闭包内,新定义的变量。
# 查看默认情况
def test():
num = 16
def test2():
num = 666
print(num)
print(num) # 查看外部函数num
test2() # 调用内部函数,查看内部函数输出结果
print(num) # 调用内部函数后,再次查看外部函数的num,发现并没有改变,说明内部的不会改变
return test2
result = test()
# 若想在内部改变
def test():
num = 16
def test2():
nonlocal num # 设置为非局部变量
num = 666
print(num)
print(num) # 查看外部函数num
test2() # 调用内部函数,查看内部函数输出结果
print(num) # 调用内部函数后,再次查看外部函数的num,发现改变了
return test2
result = test()
输出:
- 当闭包内,引用了一个,后期会发生变化的变量时,一定要注意
def test():
funcs =[]
for i in range(1,4):
def test2(num):
#num1
def inner():
print(num)
return inner
funcs.append(test2(i))
return funcs
newFuncs = test()
print(newFuncs) # 发现是存储了四次函数
newFuncs[0]() # 调用第一个
newFuncs[1]()
newFuncs[2]()
输出:
装饰器
作用:在函数名以及函数体不改变的前提下,给一个函数附加一些额外代码。
设计思路:
- 业务逻辑代码非常多就造成了每一份逻辑代码,在调用具体的功能函数之前都需要去做一个验证,代码冗余度就比较大,代码的复用性比较差,代码的维护性比较差。
- 直接在功能函数里面,去修改,方便代码的重用。
- 函数名字不能发生改变。
- 函数体内部的代码不能发生改变。
- 装饰器的执行时间是立即执行。
def check(func):
def inner():
print("登录验证操作....")
func()
return inner
@check # 在此处就执行了check函数
def fss():
print("发说说")
# @check 等于操作 fss = check(fss)
fss()
输出:
登录验证操作…
发说说
进阶使用:
- 装饰器可以叠加
从上到下装饰,从下到上执行。
# 装饰器1
def zhuangshiqi_line(func):
def inner():
print("-"*20)
func()
return inner
# 装饰器2
def zhuangshiqi_star(func):
def inner():
print("*"* 30)
func()
return inner
@zhuangshiqi_line
@zhuangshiqi_star # 等效于print_content = zhuangshigi_star(print_content)
def print_content():
print("菜就多练")
print_content()
输出:
- 对有参函数进行装饰:无论什么场景,保证函数调用参数个数一致;为了通用,可以使用不定长参数,结合拆包操作进行处理。
def zsq(func):
def inner(*args, **kwargs): # 接受随机参数,组为元组和字典
print("_"* 30)
print(args, kwargs)
func(*args, **kwargs) # 打印是要进行拆包的
return inner
@zsq
def pnum(num, num2, num3):
print(num,num2,num3)
@zsq
def pnum2(num):
print(num)
pnum(123,222,num3=666)
pnum2(999)
输出:
- 对有返回值的函数进行装饰:无论什么场景,保证函数返回值一致。
# 对有返回值的函数进行装饰:无论什么场景,保证函数返回值一致。
def zsq(func):
def inner(*args, **kwargs): # 接受随机参数,组为元组和字典
print("_"* 30)
# print(args, kwargs)
res = func(*args, **kwargs) # 打印是要进行拆包的
return res
return inner
@zsq
def pnum(num, num2, num3):
print(num,num2,num3)
return num+num2+num3
@zsq
def pnum2(num):
print(num)
res1 = pnum(123,222,num3=666)
res2 = pnum2(999)
print(res1)
print(res2)
输出:
- 带参数的装饰器
# 装饰器参数
def getzsq(char):
# 装饰器
def zsq(func):
def inner():
print(char * 30)
func()
return inner
return zsq
@getzsq("-") # 能够改变其中的前置打印参数
def f1():
print('666')
f1()
输出:
生成器
概念:
是一个特殊的迭代器(迭代器的抽象层级更高)。所以, 拥有迭代器的特性:惰性计算数据,节省内存;能够记录状态,并通过next0)函数,访问下一个状态;具备可迭代特性。但是,如果打造一个自己的迭代器,比较复杂需要实现很多方法,所以,就有一个更加优雅的方式"生成器”。
创建方式:
- 生成器表达式——把列表推导式的[]修改成()
l=(i for i in range(1,10000000) if i% 2 == 0)
print(l)
print(next(l))
print(next(l))
print(l.__next__())
输出:
- 生成器函数——函数中包含yield语句,这个函数的执行结果就是“生成器”(yield,可以去阻断当前的函数执行,然后,当使用next()数,或者,__ next__()都会让函数继续执行,然后,当执行到下一个yield语句的时候,又会被暂停)。
def test():
print("xxx")
yield 1
print("a")
yield 2
print("b")
yield 'c'
print("c")
yield 4
print("d")
yield 5
print("e")
g= test()
print(g)
print(next(g))
print(next(g))
print(next(g))
输出:
访问方式:
生成器具备可迭代特性:next()函数等价于生成器__next__()或for in
Send方法:
send方法有一个参数,指定的是上一次被挂起的yield语句的返回值相比于__next__()。可以额外的给yield 语句传值(其中注意第一次调用t.send(None))
def test():
# print("xxx")
res1=yield 1#"ooo"
print(res1)
res2 = yield 2
print(res2)
g = test()
# print(g.__next__())
# print(g.__next__())
#print(g.send("ooo"))
print(g.send(None))
print(g.send(666))
输出:
1
666
2
关闭生成器:
g.close()
后续如果继续调用,会抛出StopIteration异常提示
def test():
print("xxx")
yield 1
print("a")
yield 2
print("b")
yield 'c'
print("c")
yield 4
print("d")
yield 5
print("e")
g= test()
print(g)
print(next(g))
print(next(g))
g.close() #!!!会直接跳出
print(next(g))
输出:
注意:如果碰到return生成器只会遍历一次,会直接终止,抛出StopIteration异常提示。
递归函数
概念:函数A内部,继续调用函数A体现;传递和回归。
# 比如拿阶乘举例子
def jiecheng(n):
if n == 1:
return 1
#n!= 1
return n * jiecheng(n-1)
result = jiecheng(4)
print(result)
输出:
24
函数的作用域
变量的作用域:
指的是变量的作用范围——可操作范围;Python是静态作用域,也就是说在Python中,变量的作用域源于它在代码中的位置;在不同的位置,可能有不同的命名空间。(其中命名空间是 作用域的体现形式;不同的具体的操作范围。)
Python的空间分为:
a = 999
def test():
# 这里如果,直接使用赋值表达式,赋值给一个变量,其实是代表,定义一个新的变量
a = 6
print(a)
def tes2():
nonlocal a
a = 77
print(a)
test()
print(a)
输出:
999
6
999
全局改变:
a = 999
def test():
# 这里如果,直接使用赋值表达式,赋值给一个变量,其实是代表,定义一个新的变量
global a
a = 6
print(a)
print(a)
test()
print(a)
输出:
999
6
6
a = 999
def test():
# 这里如果,直接使用赋值表达式,赋值给一个变量,其实是代表,定义一个新的变量
a = 6
print(a)
def tes2():
nonlocal a
a = 77
print(locals())
print(globals())
# print(a)
test()
# print(a)
输出: