- 一、关于*的一些打散功能
- 二、了解函数
- 三、函数的返回值return
- 四、函数的参数
- 1.无参数的函数
- 2.有参数的函数
- 五、函数的命名空间
- 六、全局变量和局部变量
- 七、函数的作用域以及执行顺序(LEGB)
- 八、递归函数
- 九、Python的常见内置函数
- 十、匿名函数:lambda
在说函数之前我们先了解一下*
号,这个*
好在传入可变参数和接收多个值会偶尔使用到
# ---------------------------全部打散
a = [1, 2, 3, 4, 5]
b = (1, 2, 3, 4, 5)
c = {1, 2, 3, 4, 5}
d = '12345'
print(*a) # 1 2 3 4 5
print(*b) # 1 2 3 4 5
print(*c) # 1 2 3 4 5
print(*d) # 1 2 3 4 5
# -----------------------------部分打散
a = [1, 2, 3, 4, 5]
b, *c = a
print(b) # 1
print(c) # 2,3,4,5
d, *e, f = a
print(d) # 1
print(e) # [2, 3, 4]
print(f) # 5
二、了解函数
问题1:什么是函数呢?
函数是为了将相同功能的一段代码进行封装,然后我们在使用的时候不用写重复代码,只需要传入规定的参数,然后执行对应的功能即可。
下边是函数的一个基本格式
def func(argument1, argument2):#参数可有可无,可多可少
"""我是注释,一般可以写函数的功能,还有参数分别是什么 和 返回值的返回内容"""
pass
return argument1, argument2(返回值可有可无,)
问题2:那么我们函数有什么特点呢?:
- 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
- 函数必须先定义后调用,定义的时候函数代码块以
def
关键词开头,后接函数标识符名称和()
。 - 函数内容以冒号起始,并且缩进。
- 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
- return [表达式] 结束函数,选择性地返回一个值给调用方。不写return或者只写return默认返回None,并且返回值可以设置多个
- 函数的本质是调用对应函数名的内存地址
问题3:关于函数名字的定义规则?
- 字母,下划线和数字组成,且数字不能开头
- 长度任意长
- 不能和python关键字同名
就是执行该函数,返回一个或多个规定好的值
注意:
- 函数可以有1个或者多个返回值
- return后边不写返回的内容,默认返回None
- return一个作用是返回内容,另一个作用是结束函数(函数一旦执行return,函数立刻终止)
def fun():
return "hello"
print(1111) # 已经return,无法执行
x = fun()
print(x) # hello
四、函数的参数
问题:什么是函数的参数?
1.举一个例子,当我们孩子进入一个学校,我们输入孩子的名字,学校直接告诉我们孩子是不是我们学校的学生,那么我们把学校告诉我们这件事看出一个函数,然后孩子的名字就是我们需要告知学校的参数。 2.同样有的函数也可以不传参数,只执行他自己的事情,我们调用函数就只是一个触发他的过程,例如我们碰见亲爱的领导,叫了一声领导,但是领导没理你,那么这种时候只是单纯起一个叫他的过程。
关于函数参数顺序遵循: 位置参数,元组参数,默认参数,字典参数
1.无参数的函数def print_():
print('hello world')
print_() # 输出hello world
print_() # 输出hello world
2.有参数的函数
我们先了解什么是实参和形参?
- 形参就是函数名圆括号里面的参数,形参包括默认参数,可变参数
- 实参就是我们调用函数时候传入的参数,传参需要按照规定的形式填,实参包含关键字参数,顺序参数
① 位置(顺序)参数和关键字参数
def add(name, age, sex): # 此处参数位置叫形参,定义的时候没有实际的值
"""
函数执行了一个传入名字年龄性别而返回一句话的内容,返回值msg为一句话
"""
msg = f'我叫{name},年龄{age}岁,性别是{sex}生。'
return msg
# ---------------------实参:此处调用函数括号的参数叫实参,因为此处的参数有实际的值
# 位置参数:传入值的顺序要与实际参数相对应,那么这种一般按顺序对应的就叫位置参数
result =add('彪子', '18', '女')
print(result) # 我叫彪子,年龄18岁,性别是女生。
# 关键词参数:关键词参数可以不按原函数定义参数顺序写。但是关键字参数和位置参数同时使用时候,关键字参数必须放在位置参数后边
result1 = add(name='彪子', age='18', sex='女') # 全部关键字参数
print(result1) # 我叫彪子,年龄18岁,性别是女生。
result2 = add('彪子', '18', sex='女') # 部分关键字参数
print(result2) # 我叫彪子,年龄18岁,性别是女生。
②默认参数
- 默认参数要有不可变类型的对象,如果是可变类型,可能有逻辑错误。
- 默认参数针对的形参
def fun(name,age,sex='男'):
print('名字:',name)
print('年龄:',age)
print('性别:', sex)
fun('张三',18 ) #sex不写,则按函数定义时候的默认参数 男 进行赋值
fun('李四',18,'女') #sex写后,会把默认参数的值重新赋值
结果: ④可变参数
*args
,一个*号,传入的为元组形式,即使传入一个值也会为元组
def fun(*args):
print(args,type(args))
fun()
fun(1)
fun(1,2,3,4)
结果:
**kwargs
,传入的是字典参数(传入形式: 键值名=参数)
def fun(**kwargs):
print(kwargs,type(kwargs))
fun(name='张三',age=18)
结果:
Python中的三种命名空间
内置命名空间 —— python解释器
- 就是python解释器一启动就可以使用的名字存储在内置命名空间中
- 内置的名字在启动解释器的时候被加载进内存里
全局命名空间 —— 我们写的代码但不是函数中的代码
- 是在程序从上到下被执行的过程中依次加载进内存的
- 放置了我们设置的所有变量名和函数名
局部命名空间 —— 函数
- 就是函数内部定义的名字
- 当调用函数的时候 才会产生这个名称空间 随着函数执行的结束 这个命名空间就又消失了
注意:
- 在局部:可以使用全局、内置命名空间中的名字
- 在全局:可以使用内置命名空间中的名字,但是不能用局部中使用
- 在内置:不能使用局部和全局的名字的
问题如果我们在外部想使用局部的变量怎么办呢?
global()
函数可以访问全局命名空间locals()
可以访问局部名称空间
而且我们使用global可以在局部改变全局的a值(前提是本身函数内部没有定义a)
问题:如何实现局部变量和全局变量的互相修改呢?
- global可以将局部变量变为全局变量
- nonlocal可以修改外层(非全局)变量
global使用
def fun():
global a
a+=20
print('函数内部:',a)
a=10
fun()
print('函数外部',a)
结果:
nonlocal使用
def fun():
a=9
def fun1():#--------------函数嵌套enclosing
nonlocal a#-------------可以修改外层(非全局)的变量
a=98#--------------就是将外层的a=9改变为98
print('我是内部',a)
fun1()
print('我是外部',a)
fun()
结果:
问题:函数的作用域,是指一个参数在定义后的使用区间,如果一个函数调用了一个值,那么函数是怎么在代码里找到这个值呢?
这是一个函数套函数的形式,我们作用域遵循的原则就是LEGB
a = 1
b = 2
c = 3
def fun_out():
a = 10
b = 20
def fun_in():
a = 100
print(a)#-----------L Local(函数内部)首先查找局部作用域,有则输出,没有找E
print(b)#-----------E Enclosing(嵌套函数外层函数的内部)L没有该值就查找嵌套作用域,有则输出,没有向G
print(c)#-----------G Global(模块全局)E没有该值找全局作用域G,有则输出,没有向B
print(__name__)#----B Built-in(内建作用域)最后一步,查找内建作用域,,有则输出,没有报错
fun_in()
fun_out()
结果:
- 递归函数是函数的高阶使用了,就是通过规则函数重复的调用自己本身,然后直到符合自己的结束条件参会结束。
- 但是不能一直重复调用,所以我们需要设置一个明确的结束递归条件,也叫递归出口
递归函数经典案例:斐波那契数列
假定每对大兔每月能生产一对小兔,而每对小兔生长两个月就成为大兔.这个问题导致了著名的数列:1,1,2,3,5,8,13,21,34,55,89,144,233,·…它是一个线性递归数列,那么比如我们初始一只兔子,那么指定月份后,我们有多少只兔子呢
def fbnq(n):
if n == 1 or n == 2:
return 1
else:
return fbnq(n - 1) + fbnq(n - 2)
n = int(input('请输入月份:'))
for i in range(1,n+1):
print(i, '月兔子有:', fbnq(i))
结果:
1.求绝对值的函数:abs()
a=-1
print(abs(a)) # 1
2.求最大值得函数:max()
# 1.无key
a=[0,2,4,2,1,3]
print(max(a))# 返回最大的4
# 2.有key的max(参数,key=函数名)
a=[-1,-2,-4,-6]
b=max(a,key=abs)# 比较的时候按绝对值比较,输出原数值
print(b)
3.列表的内置排序函数(默认升序):sort()
①无key的sort()
b = [-1, -4, -2, -3]
b.sort()
print(b)#----------------------------------[-4,-3,-2,-1]
②有key的sort(参数,key=函数名)
a = [-1, -4, -2, -3]
a.sort(key=abs)#都是升序,只是按执行完的绝对值方法进行的升序,还是写的原值
print(a)#----------------------------------[-1, -2, -3, -4]
③降序
a = [-1, -4, -2, -3]
a.sort(reverse=True)
print(a )# -1,-2,-3,-4]
4.map(函数名,可迭代内容)
会依次将可迭代内容的每个元素执行该函数,并且返回新的值
x = [1, 2, 3, 4, 5]
def fun(x):
return x * x
b = map(fun, x)
# 本身返回一个map对象,需要转换
print(list(b))# [1, 4, 9, 16, 25]
5.filter(函数名,序列) 会依次将可迭代内容的每个元素执行该函数返回True或False,True的放到新列表,过滤不符合要求的元素,将符合元素放在一个新列表中
# 过滤奇数的方法
def fun(x):
if x%2==0:
return True
else:
return False
a=[1,2,3,4,5,6]
b=filter(fun,a)
# 本身返回一个filter对象,需要转换
print(list(b))#-------------------------------------------[2,4,6]
6.zip(序列1,序列2,序列3…) 将序列的各个对应元素一一打包成元组,按照最短的算
a=[1,2,3,4]#按照最短的,一一匹配
b=('a','b','c')
c=('啊','哦','窝')
d=zip(a,b,c)
for i in d:
print(i)
结果:
1.基本定义
-
形式:
变量名=lambda 参数1,参数2...:表达式
-
注意:
- 匿名函数的表达式参数是未知参数
- 匿名不能包含循环,return,可以包含if-else
- lambda表达式会产生一个新的局部作用域
-
优点: 代码简洁,不增加额外变量
-
缺点: 难于理解,降低了可读性
2.使用示例
举例1
hs = lambda x, y: x + y
b = hs(2, 3)
print(b) # -----------------------------5
举例2:匿名函数和三元表达式的搭配
max = lambda a, b: a if a > b else b
print(max(3, 4)) # 4
举例3: 匿名函数求列表的偶数
a = [1, 2, 3, 4, 5, 6, 7]
result = list(filter(lambda a: True if a % 2 == 0 else False, a))
print(result) # [2, 4, 6]
3.匿名函数常见考题
题1:
def hehe():
a=[]
for i in range(3):
a.append(lambda y:y*i)
return a
z=hehe()
print(z[0](2))#--------------------------4
print(z[1](2))#------------------------4
print(z[2](2))#------------------------------4
原因:因为嵌套作用域的变量在嵌套的函数被调用时才会查找,所以他们实际上是统一的值(最后一次循环变量的值)。 理解方法1:相当于每次往列表里传入一个 lambda y:y * i 的表达式,而这个i真正是:调用的最后一次循环的i 理解方法2:比如第一次循环,往列表添加 lambda y:y * i 的表达式,此时i指向的是0,第二次,又往列表添加一个 lambda y:y*i 的表达式,此时i指向新的1,把原先的0覆盖…,最后指向4
若要改成预期结果,那么只需按照下边的改,或者是将其改造成一个生成器,不会让其提前运行也可
def hehe():
a = []
for i in range(3):
a.append(lambda y, i=i: y * i)#---------此处的i,是默认参数,会自动赋值
return a
z = hehe()
print(z[0](2))#--------------------------------0
print(z[1](2))#-------------------------------2
print(z[2](2))#-----------------------4
原因:i在每次运行时,都会把遇到的i=常数,赋值给匿名函数